# Instalar el paquete si no está instalado
if (!require("openxlsx")) install.packages("openxlsx")
Loading required package: openxlsx
if (!require("dplyr")) install.packages("dplyr")
Loading required package: dplyr

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
# Cargar el paquete
library(dplyr)

1) Fuente de datos

Carga el dataset

library(openxlsx)
delitos <- read.xlsx("/Users/bquinonez/Downloads/delitos_2023.xlsx")
delitos
summary(delitos)
     id-sum            anio          mes                dia                fecha           franja     
 Min.   :     1   Min.   :2023   Length:157461      Length:157461      Min.   :44927   Min.   : 0.00  
 1st Qu.: 39366   1st Qu.:2023   Class :character   Class :character   1st Qu.:45021   1st Qu.: 8.00  
 Median : 78731   Median :2023   Mode  :character   Mode  :character   Median :45113   Median :13.00  
 Mean   : 78731   Mean   :2023                                         Mean   :45112   Mean   :12.95  
 3rd Qu.:118096   3rd Qu.:2023                                         3rd Qu.:45204   3rd Qu.:18.00  
 Max.   :157461   Max.   :2023                                         Max.   :45291   Max.   :23.00  
     tipo             subtipo            uso_arma           uso_moto            barrio         
 Length:157461      Length:157461      Length:157461      Length:157461      Length:157461     
 Class :character   Class :character   Class :character   Class :character   Class :character  
 Mode  :character   Mode  :character   Mode  :character   Mode  :character   Mode  :character  
                                                                                               
                                                                                               
                                                                                               
    comuna            latitud            longitud            cantidad
 Length:157461      Length:157461      Length:157461      Min.   :1  
 Class :character   Class :character   Class :character   1st Qu.:1  
 Mode  :character   Mode  :character   Mode  :character   Median :1  
                                                          Mean   :1  
                                                          3rd Qu.:1  
                                                          Max.   :1  
dim(delitos)
[1] 157461     15

El dataset antes del preprocesamiento cuenta con 15 columnas y 157.461 registros correspondientes al año 2023.

2) Preprocesamiento

Verificación de nulos

colSums(is.na(delitos))
  id-sum     anio      mes      dia    fecha   franja     tipo  subtipo uso_arma uso_moto   barrio   comuna 
       0        0        0        0        0        0        0        0        0        0       35        0 
 latitud longitud cantidad 
       9        9        0 

Tipos de Delitos

cantidad_valores_unicos <- (unique(delitos$tipo))
print(cantidad_valores_unicos)
[1] "Vialidad"   "Lesiones"   "Amenazas"   "Hurto"      "Robo"       "Homicidios"

Subtipos de delitos

conteo_por_tipo <- table(delitos$tipo)
print(conteo_por_tipo)

  Amenazas Homicidios      Hurto   Lesiones       Robo   Vialidad 
      8332         91      62567      11045      64983      10443 
cantidad_valores_unicos <- (unique(delitos$subtipo))
print(cantidad_valores_unicos)
[1] "Muertes por siniestros viales"  "Lesiones por siniestros viales" "Lesiones Dolosas"              
[4] "Amenazas"                       "Hurto total"                    "Robo total"                    
[7] "Hurto automotor"                "Robo automotor"                 "Homicidio Doloso"              
conteo_por_subtipo <- table(delitos$subtipo)
print(conteo_por_subtipo)

                      Amenazas               Homicidio Doloso                Hurto automotor 
                          8332                             91                           4494 
                   Hurto total               Lesiones Dolosas Lesiones por siniestros viales 
                         58073                          11045                          10339 
 Muertes por siniestros viales                 Robo automotor                     Robo total 
                           104                            904                          64079 

Creación de variables

Agregamos la columna de franja horaria

library(dplyr)
#Genero la columna franja(horaria)
delitos <- delitos %>%
  mutate(
    franja_horaria = case_when(
      franja >= 6 & franja < 12 ~ "Mañana",
      franja >= 12 & franja < 18 ~ "Tarde",
      franja >= 18 & franja < 24 ~ "Noche",
      franja >= 0 & franja < 6  ~ "Madrugada",
      TRUE ~ "Desconocido" # Para casos fuera de rango 
    )
  )
delitos

Agregamos una columna que indica si es fin de semana o día de semana

delitos$tipo_dia <- ifelse(delitos$dia %in% c("SAB", "DOM"),
                                             "Fin de semana", "Día de semana")
delitos

Agregamos una columna que indica la estación del año

# Crear columna 'estacion' basada en el mes
delitos <- delitos %>%
  mutate(
    estacion = case_when(
      mes %in% c("diciembre", "enero", "febrero") ~ "Verano",
      mes %in% c("marzo", "abril", "mayo")       ~ "Otoño",
      mes %in% c("junio", "julio", "agosto")     ~ "Invierno",
      mes %in% c("septiembre", "octubre", "noviembre") ~ "Primavera",
      TRUE ~ "Desconocido" # Para casos no esperados
    )
  )
delitos

Conversión de longitud y latitud

# Convertir longitud y latitud a numéricas y escalarlas correctamente
delitos <- delitos %>%
  mutate(
    longitud = as.numeric(longitud) / 1e6,  # Escalar dividiendo entre 10^6
    latitud = as.numeric(latitud) / 1e6    # Escalar dividiendo entre 10^6
  )
Warning: There were 2 warnings in `mutate()`.
The first warning was:
ℹ In argument: `longitud = as.numeric(longitud)/1e+06`.
Caused by warning:
! NAs introduced by coercion
ℹ Run ]8;;ide:run:dplyr::last_dplyr_warnings()dplyr::last_dplyr_warnings()]8;; to see the 1 remaining warning.
# Verificar las primeras filas para confirmar
head(delitos[c("longitud", "latitud")])

Eliminación de nulos y barrios que no forman parte de CABA

library(stringr)
delitos <- na.omit(delitos)
dim(delitos)
[1] 154521     18
delitos <- delitos %>% 
  filter(barrio != "0", barrio != "Sin geo", barrio != "NULL", barrio != "NO ESPECIFICADA", barrio != "GREGORIO DE LAFERRERE") %>%
  mutate(barrio = str_replace_all(barrio, "NUNEZ", "NUÑEZ"))

Luego de la limpieza quedaron un total de 154521 y 18 atributos, ya que arreglamos 3 variables adicionales: franja_horaria, dia y estacion

Gráficos exploratorios

Distribución de Delitos por Tipo y Mes

library(ggplot2)
library(dplyr)

# Agrupar los datos
delitos_agrupados_tipo <- delitos %>%
  group_by(mes, tipo) %>%
  summarise(total = n(), .groups = "drop")

# Crear el gráfico sin números
grafico_apilado <- ggplot(delitos_agrupados_tipo, aes(x = mes, y = total, fill = tipo)) +
  geom_bar(stat = "identity", position = "stack") +
  facet_wrap(~ tipo, scales = "free_y") +  # Dividir por tipo de delito
  labs(
    title = "Distribución de Delitos por Tipo y Mes",
    x = "Mes",
    y = "Número de Delitos"
  ) +
  theme_minimal(base_size = 14) +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "bottom",
    strip.text = element_text(size = 12),  # Tamaño del título de cada faceta
    plot.title = element_text(hjust = 0.5),  # Centrar el título
    legend.text = element_text(size = 10),  # Ajustar tamaño de texto de la leyenda
    legend.key.size = unit(0.8, "cm")  # Tamaño de las cajas de la leyenda
  ) +
  scale_fill_brewer(palette = "Set3")  # Paleta de colores diferenciados

# Mostrar el gráfico
print(grafico_apilado)

Distribución de Delitos por Tipo y Franja Horaria

library(ggplot2)
library(dplyr)

# Agrupar los datos por franja horaria, tipo y subtipo
delitos_agrupados_horario <- delitos %>%
  group_by(franja, tipo) %>%
  summarise(total = n(), .groups = "drop")

# Crear el gráfico apilado por franja horaria
grafico_horarios <- ggplot(delitos_agrupados_horario, aes(x = franja, y = total, fill = tipo)) +
  geom_bar(stat = "identity", position = "stack") +
  facet_wrap(~ tipo, scales = "free_y") +  # Dividir por tipo de delito
  labs(
    title = "Distribución de Delitos por Tipo y Franja Horaria",
    x = "Franja Horaria",
    y = "Número de Delitos"
  ) +
  theme_minimal(base_size = 14) +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "bottom",
    strip.text = element_text(size = 12),  # Tamaño del título de cada faceta
    plot.title = element_text(hjust = 0.5),  # Centrar el título
    legend.text = element_text(size = 10),  # Ajustar tamaño de texto de la leyenda
    legend.key.size = unit(0.8, "cm")  # Tamaño de las cajas de la leyenda
  ) +
  scale_fill_brewer(palette = "Set3")  # Paleta de colores diferenciados

# Mostrar el gráfico
print(grafico_horarios)

library(ggplot2)
library(dplyr)

# Agrupar los datos por franja horaria
delitos_por_hora <- delitos %>%
  group_by(franja) %>%
  summarise(total = n(), .groups = "drop")

# Crear el gráfico de barras
grafico_horas <- ggplot(delitos_por_hora, aes(x = franja, y = total)) +
  geom_bar(stat = "identity", fill = "steelblue", color = "black", width = 0.7) +
  labs(
    title = "Distribución de Delitos por Hora",
    x = "Hora del Día",
    y = "Número de Delitos"
  ) +
  theme_minimal(base_size = 14) +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    plot.title = element_text(hjust = 0.5),
    panel.grid.major = element_line(size = 0.5, linetype = "dotted"),
    panel.grid.minor = element_blank()
  )
Warning: The `size` argument of `element_line()` is deprecated as of ggplot2 3.4.0.
Please use the `linewidth` argument instead.
# Mostrar el gráfico
print(grafico_horas)

Transformación de variables categóricas

Transformamos todos los caracteres a numéricos, exceptuando moto,arma,lat,long

library(dplyr)

# Convertir columnas específicas `uso_arma` y `uso_moto` (Sí=1, No=0)
del <- delitos %>%
  mutate(
    uso_arma = ifelse(uso_arma == "SI", 1, 0),
    uso_moto = ifelse(uso_moto == "SI", 1, 0)
  )

# Convertir otras columnas categóricas a nuevas columnas codificadas
del <- del %>%
  mutate(across(
    .cols = where(is.character) & !all_of(c("longitud", "latitud", "uso_arma", "uso_moto")), 
    .fns = ~ as.numeric(as.factor(.)),
    .names = "{.col}_cod"
  )) %>%
  mutate(across(
    .cols = where(is.factor) & !all_of(c("longitud", "latitud", "uso_arma", "uso_moto")), 
    .fns = ~ as.numeric(.),
    .names = "{.col}_cod"
  ))

# Verificar la estructura del dataset después de las transformaciones
str(del)
'data.frame':   153981 obs. of  27 variables:
 $ id-sum            : num  1 2 3 4 5 7 9 10 14 16 ...
 $ anio              : num  2023 2023 2023 2023 2023 ...
 $ mes               : chr  "enero" "enero" "enero" "enero" ...
 $ dia               : chr  "LUN" "MIE" "VIE" "VIE" ...
 $ fecha             : num  44928 44937 44939 44939 44939 ...
 $ franja            : num  19 11 4 6 6 9 11 18 5 19 ...
 $ tipo              : chr  "Vialidad" "Vialidad" "Vialidad" "Vialidad" ...
 $ subtipo           : chr  "Muertes por siniestros viales" "Muertes por siniestros viales" "Muertes por siniestros viales" "Muertes por siniestros viales" ...
 $ uso_arma          : num  0 0 0 0 0 0 0 0 0 0 ...
 $ uso_moto          : num  0 0 0 0 0 0 0 0 0 0 ...
 $ barrio            : chr  "BELGRANO" "VILLA LUGANO" "SAAVEDRA" "PARQUE CHACABUCO" ...
 $ comuna            : chr  "13" "8" "12" "7" ...
 $ latitud           : num  -58.4 -58.5 -58.5 -58.5 -58.5 ...
 $ longitud          : num  -34.6 -34.7 -34.5 -34.6 -34.6 ...
 $ cantidad          : num  1 1 1 1 1 1 1 1 1 1 ...
 $ franja_horaria    : chr  "Noche" "Mañana" "Madrugada" "Mañana" ...
 $ tipo_dia          : chr  "Día de semana" "Día de semana" "Día de semana" "Día de semana" ...
 $ estacion          : chr  "Verano" "Verano" "Verano" "Verano" ...
 $ mes_cod           : num  4 4 4 4 4 4 5 5 8 8 ...
 $ dia_cod           : num  3 5 7 7 7 4 1 5 4 3 ...
 $ tipo_cod          : num  6 6 6 6 6 6 6 6 6 6 ...
 $ subtipo_cod       : num  7 7 7 7 7 7 7 7 7 7 ...
 $ barrio_cod        : num  6 48 36 29 29 3 36 6 56 5 ...
 $ comuna_cod        : num  5 14 4 13 13 9 4 5 14 10 ...
 $ franja_horaria_cod: num  3 2 1 2 2 2 2 3 1 3 ...
 $ tipo_dia_cod      : num  1 1 1 1 1 1 2 1 1 1 ...
 $ estacion_cod      : num  4 4 4 4 4 4 4 4 2 2 ...
 - attr(*, "na.action")= 'omit' Named int [1:2940] 6 8 11 12 13 15 17 27 32 35 ...
  ..- attr(*, "names")= chr [1:2940] "6" "8" "11" "12" ...

Evolución Temporal de Delitos por Tipo

library(ggplot2)
library(dplyr)

# Agrupación por año, mes y tipo de delito
delitos_temporal <- del %>%
  group_by(anio, mes_cod, tipo) %>%
  summarise(total_delitos = n(), .groups = "drop")

# Visualización de la evolución
ggplot(delitos_temporal, aes(x = mes_cod, y = total_delitos, color = tipo, group = tipo)) +
  geom_line(size = 1) +
  scale_x_continuous(breaks = 1:12, labels = month.abb) +
  labs(title = "Evolución Temporal de Delitos por Tipo por mes",
       x = "Mes",
       y = "Número de Delitos",
       color = "Tipo de Delito") +
  theme_minimal()
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
Please use `linewidth` instead.

Evolución Temporal de Delitos por Tipo

delitos_temporal_h <- del %>%
  group_by(franja, tipo) %>%
  summarise(total_delitos = n(), .groups = "drop")

delitos_temporal_h$franja <- as.character(delitos_temporal_h$franja)


ggplot(delitos_temporal_h, aes(x = franja, y = total_delitos, color = tipo, group = tipo)) +
  geom_line(size = 1) +
  labs(title = "Evolución Temporal de Delitos por Tipo y franja horaria",
       x = "Franja Horaria",
       y = "Número de Delitos",
       color = "Tipo de Delito") +
  theme_minimal()

NA
NA

Frecuencia de Delitos por Barrio y Tipo

library(ggplot2)
library(dplyr)

# Agrupar los datos por barrio y calcular el total de delitos por barrio
delitos_por_barrio <- delitos %>%
  group_by(barrio) %>%
  summarise(total_delitos = n(), .groups = "drop") %>%
  mutate(barrio = reorder(barrio, -total_delitos))  # Ordenar barrios por número de delitos

# Filtrar los 15 barrios con más delitos
top_barrios <- delitos_por_barrio

# Crear el gráfico de barras
ggplot(top_barrios, aes(x = barrio, y = total_delitos)) +
  geom_bar(stat = "identity", fill = "steelblue") +  # Color sólido para las barras
  labs(
    title = "Frecuencia Total de Delitos por Barrio",
    x = "Barrio",
    y = "Número de Delitos"
  ) +
  theme_minimal() +
  theme(
    axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1),  # Rotar etiquetas a 90 grados
    plot.title = element_text(hjust = 0.5)  # Centrar el título
  )

# Guardar el gráfico con dimensiones mayores
ggsave("grafico_delitos_totales_barrio.png", width = 12, height = 8, dpi = 300)

NA
NA

Dia de semana vs Finde

# Agrupar los datos por día de la semana
delitos_por_dia <- delitos %>%
  group_by(tipo_dia) %>%
  summarise(total = n(), .groups = "drop")

# Crear el gráfico de barras
ggplot(delitos_por_dia, aes(x = tipo_dia, y = total, fill = tipo_dia)) +
  geom_bar(stat = "identity", width = 0.7) +
  labs(
    title = "Frecuencia de Delitos por Día de la Semana",
    x = "Día de la Semana",
    y = "Número de Delitos"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5),  # Centrar el título
    axis.text.x = element_text(angle = 45, hjust = 1)  # Rotar etiquetas si es necesario
  ) +
  scale_fill_brewer(palette = "Set3")  # Paleta de colores

NA
NA

Frecuencia de Delitos por Estación del Año

# Agrupar los datos por estación
delitos_por_estacion <- delitos %>%
  group_by(estacion) %>%
  summarise(total = n(), .groups = "drop")

# Crear el gráfico de barras
ggplot(delitos_por_estacion, aes(x = estacion, y = total, fill = estacion)) +
  geom_bar(stat = "identity") +
  labs(
    title = "Frecuencia de Delitos por Estación del Año",
    x = "Estación",
    y = "Número de Delitos"
  ) +
  theme_minimal()

3) Aplicación de FDA

A) Frencuencia de delitos agrupados por franja (24) y fecha

# Cargar librerías necesarias
if (!require("RColorBrewer")) install.packages("RColorBrewer")
Loading required package: RColorBrewer
library(RColorBrewer)
library(dplyr)
library(tidyr)
library(fda.usc)
Loading required package: fda
Loading required package: splines
Loading required package: fds
Loading required package: rainbow
Loading required package: MASS

Attaching package: ‘MASS’

The following object is masked from ‘package:dplyr’:

    select

Loading required package: pcaPP
Loading required package: RCurl

Attaching package: ‘RCurl’

The following object is masked from ‘package:tidyr’:

    complete

Loading required package: deSolve

Attaching package: ‘fda’

The following object is masked from ‘package:graphics’:

    matplot

Loading required package: mgcv
Loading required package: nlme

Attaching package: ‘nlme’

The following object is masked from ‘package:dplyr’:

    collapse

This is mgcv 1.9-1. For overview type 'help("mgcv-package")'.
Loading required package: knitr
 fda.usc is running sequentially usign foreach package
 Please, execute ops.fda.usc() once to run in local parallel mode
 Deprecated functions: min.basis, min.np, anova.hetero, anova.onefactor, anova.RPm
 New functions: optim.basis, optim.np, fanova.hetero, fanova.onefactor, fanova.RPm
----------------------------------------------------------------------------------
library(fda)
library(ggplot2)
library(ggrepel)
library(tibble)  # Para usar column_to_rownames


# Agrupar por día y horario, y contar la frecuencia de delitos
delitos_por_dia_horario <- delitos %>%
  group_by(franja, fecha) %>%  # 'franja_horaria' representa los horarios
  summarise(frecuencia = sum(cantidad, na.rm = TRUE), .groups = "drop")

# Crear una matriz con filas como días y columnas como franjas horarias
delitos_franja_matrix <- delitos_por_dia_horario %>%
  pivot_wider(names_from = fecha, values_from = frecuencia, values_fill = 0) %>%
  column_to_rownames(var = "franja") %>%
  as.matrix()

delitos_franja_matrix <- delitos_franja_matrix[complete.cases(delitos_franja_matrix), ]

a) SIN FDA: Creamos las curvas por franja a lo largo del 2023 sin FDA.


# Crear un vector de fechas reales
fechas_date <- seq.Date(from = as.Date("2023-01-01"), by = "day", length.out = ncol(delitos_franja_matrix))

# Crear colores únicos para cada barrio
colores <- rainbow(nrow(delitos_franja_matrix))


# Crear el gráfico con dimensiones ampliadas
png("grafico_franjas_sin_fda.png", width = 2000, height = 1200, res = 150)  # Resolución más alta

# Ajustar márgenes: más espacio derecho para la leyenda
par(mar = c(10, 35, 4, 4) + 0.1)  # Aumentar margen izquierdo

# Graficar las curvas originales
matplot(fechas_date, t(delitos_franja_matrix), type = "l", lty = 1, col = colores, lwd = 2,
        xlab = "Fecha", ylab = "Frecuencia", xaxt = "n",
        main = "Curvas por Franja (Sin suavizado)")
NULL
# Crear etiquetas de fechas en el eje X
fechas_etiquetas <- seq(min(fechas_date), max(fechas_date), by = "2 weeks")  # Mostrar cada dos semanas
axis(1,
     at = as.numeric(fechas_etiquetas),  # Usar fechas reales como índices
     labels = format(fechas_etiquetas, "%d %b"),  # Día y mes
     las = 2,  # Rotar las etiquetas
     cex.axis = 0.8)  # Tamaño del texto en el eje Xpe

# Crear una leyenda más amplia en el margen izquierdo
legend("topright",
       inset = c(1.2, 0),   # Más espacio hacia la izquierda
       rownames(delitos_franja_matrix),
       col = colores,
       lty = 1,
       lwd = 4,  # Grosor de las líneas
       ncol = 2,  # Dividir la leyenda en dos columnas
       cex = 0.9,  # Tamaño del texto más grande
       xpd = TRUE)  # Permitir que la leyenda esté fuera del área de dibujo

# Guardar y cerrar el archivo PNG
dev.off()
null device 
          1 
# Verificar que el archivo fue creado
browseURL("grafico_franjas_sin_fda.png")
Curvas por Franja Sin FDA ni suavizado
Curvas por Franja Sin FDA ni suavizado

b) CON FDA: Creamos las curvas por franja a lo largo del 2023 con FDA.

# Paso 2: Representar Datos como Curvas Funcionales con Suavizado

# Crear una base B-spline penalizada para suavizar las funciones
nbasis <- 25  # Ajusta el número de bases para mayor flexibilidad
lambda <- 1e-2  # Parámetro de suavizado; menor valor = más suave

# Crear la base B-spline
basis <- create.bspline.basis(rangeval = range(fechas_date), nbasis = nbasis)

# Definir el objeto de parámetros funcionales con suavizado
fd_par <- fdPar(basis, Lfdobj = int2Lfd(2), lambda = lambda)

# Convertir las filas de la matriz en funciones continuas suavizadas
fd_obj_suavizado <- smooth.basis(fechas_date, t(delitos_franja_matrix), fd_par)$fd

# Visualizar las funciones suavizadas por barrio antes del clustering
colores <- rainbow(nrow(delitos_franja_matrix))  # Generar colores únicos para cada barrio

# Crear el gráfico con dimensiones ampliadas
png("grafico_franjas_con_fda.png", width = 2000, height = 1200, res = 150)  # Resolución más alta

# Ajustar márgenes: más espacio derecho para la leyenda
par(mar = c(10, 4, 4, 35) + 0.1)  # Aumentar margen derecho para que la leyenda no quede cortada

# Graficar las funciones suavizadas
plot(fd_obj_suavizado, main = "Curvas por Franja suavizadas con FDA",
     xlab = "Fecha", ylab = "Frecuencia",
     col = colores,
     lwd = 2, xaxt = "n")  # Desactivar eje X predeterminado
[1] "done"
# Crear etiquetas de fechas en el eje X
fechas_etiquetas <- seq(min(fechas_date), max(fechas_date), by = "2 weeks")  # Mostrar cada dos semanas
axis(1,
     at = as.numeric(fechas_etiquetas),  # Usar fechas reales como índices
     labels = format(fechas_etiquetas, "%d %b"),  # Día y mes
     las = 2,  # Rotar las etiquetas
     cex.axis = 0.8)  # Tamaño del texto en el eje X

# Crear una leyenda ajustada en el lado derecho del gráfico
legend("topleft",
       inset = c(1.1, 0),  # Reducir el espacio hacia la derecha
       legend = rownames(delitos_franja_matrix),
       col = colores,
       lty = 1,
       lwd = 4,  # Grosor de las líneas
       ncol = 2,  # Dividir la leyenda en dos columnas
       cex = 0.9,  # Tamaño del texto ajustado
       xpd = TRUE)  # Permitir que la leyenda esté fuera del área de dibujo


# Guardar y cerrar el archivo PNG
dev.off()
null device 
          1 
# Verificar que el archivo fue creado
browseURL("grafico_franjas_con_fda.png")
Curvas por Franja suavizadas con FDA
Curvas por Franja suavizadas con FDA

B) Frencuencia de delitos agrupados por barrio (48) y fecha

Armamos la agrupación por barrio y fecha y creamos la matriz funcinal con barrios como filas y fechas como columnas

# Cargar librerías necesarias
if (!require("RColorBrewer")) install.packages("RColorBrewer")
library(RColorBrewer)
library(dplyr)
library(tidyr)
library(fda.usc)
library(fda)
library(ggplot2)
library(ggrepel)
library(tibble)  # Para usar column_to_rownames

# Paso 1: Agrupar datos y preparar la matriz funcional
# Agrupar por barrio y fecha, sumando la frecuencia de delitos
delitos_agrupados_barrio <- delitos %>%
  group_by(barrio, fecha) %>%
  summarise(frecuencia_diaria = sum(cantidad, na.rm = TRUE), .groups = "drop")

# Crear matriz funcional: barrios como filas, fechas como columnas
delitos_matrix <- delitos_agrupados_barrio %>%
  pivot_wider(names_from = fecha, values_from = frecuencia_diaria, values_fill = 0) %>%
  column_to_rownames(var = "barrio") %>%
  as.matrix()

# Eliminar filas con valores faltantes y verificar la variabilidad
delitos_matrix <- delitos_matrix[complete.cases(delitos_matrix), ]
variabilidad <- apply(delitos_matrix, 1, var, na.rm = TRUE)
delitos_matrix <- delitos_matrix[variabilidad > 0.01, ]

a) SIN FDA: Creamos las curvas por barrio a lo largo del 2023 sin FDA.


# Crear un vector de fechas reales
fechas_date <- seq.Date(from = as.Date("2023-01-01"), by = "day", length.out = ncol(delitos_matrix))

# Graficar curvas originales por barrio sin aplicar FDA
# Crear el gráfico con dimensiones ampliadas
png("grafico_barrios_sin_fda.png", width = 2000, height = 1200, res = 150)  # Resolución más alta

# Ajustar márgenes: más espacio izquierdo para la leyenda
par(mar = c(10, 35, 4, 4) + 0.1)  # Aumentar margen izquierdo

# Crear colores únicos para cada barrio
colores <- rainbow(nrow(delitos_matrix))

# Graficar las curvas originales
matplot(fechas_date, t(delitos_matrix), type = "l", lty = 1, col = colores, lwd = 2,
        xlab = "Fecha", ylab = "Frecuencia", xaxt = "n",
        main = "Curvas por Barrio (Sin suavizado)")
NULL
# Crear etiquetas de fechas en el eje X
fechas_etiquetas <- seq(min(fechas_date), max(fechas_date), by = "2 weeks")  # Mostrar cada dos semanas
axis(1,
     at = as.numeric(fechas_etiquetas),  # Usar fechas reales como índices
     labels = format(fechas_etiquetas, "%d %b"),  # Día y mes
     las = 2,  # Rotar las etiquetas
     cex.axis = 0.8)  # Tamaño del texto en el eje Xpe

# Crear una leyenda más amplia en el margen izquierdo
legend("topright",
       inset = c(1.2, 0),   # Más espacio hacia la izquierda
       rownames(delitos_matrix),
       col = colores,
       lty = 1,
       lwd = 4,  # Grosor de las líneas
       ncol = 2,  # Dividir la leyenda en dos columnas
       cex = 0.9,  # Tamaño del texto más grande
       xpd = TRUE)  # Permitir que la leyenda esté fuera del área de dibujo

# Guardar y cerrar el archivo PNG
dev.off()
null device 
          1 
# Verificar que el archivo fue creado
browseURL("grafico_barrios_sin_fda.png")
Curvas por Barrio (Sin FDA ni suavizado)
Curvas por Barrio (Sin FDA ni suavizado)

Graficamos el método de codo y silhouette para definir el número de clusters

dim(delitos_matrix)  # Deberías ver (n_barrio, n_fechas)
[1]  48 365
delitos_matrix_normalized <- scale(delitos_matrix)

library(factoextra)
Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
# Método del codo para determinar el número óptimo de clusters
fviz_nbclust(delitos_matrix_normalized, kmeans, method = "wss") +
  labs(title = "Método del Codo")


fviz_nbclust(delitos_matrix_normalized, kmeans, method = "silhouette") +
  labs(title = "Índice de Silhouette")

set.seed(123)  # Para reproducibilidad
num_clusters <- 2  # Cambia según los resultados del método del codo o Silhouette

kmeans_result <- kmeans(delitos_matrix_normalized, centers = num_clusters, nstart = 20)

clusters <- data.frame(
  barrio = rownames(delitos_matrix),
  cluster = kmeans_result$cluster
)

# Ordenar el dataframe por cluster
clusters_ordenados <- clusters %>%
  arrange(cluster)


# Ver los primeros resultados
print(clusters_ordenados)
NA
NA

Usamos PCA para graficar los clusters


library(ggplot2)
library(dplyr)

# Calcular la cantidad de barrios por cluster
cluster_sizes <- clusters %>%
  group_by(cluster) %>%
  summarise(n_barrios = n()) %>%
  mutate(cluster_label = paste0("Cluster ", cluster, " (n=", n_barrios, ")"))

# Calcular PCA en la matriz normalizada
pca_result <- prcomp(delitos_matrix_normalized, center = TRUE, scale. = TRUE)
pca_data <- as.data.frame(pca_result$x[, 1:2])  # Tomar las dos primeras componentes
pca_data$cluster <- as.factor(clusters$cluster)  # Convertir a factor

cluster_sizes$cluster <- as.factor(cluster_sizes$cluster)  # Convertir a factor

# Combinar con las etiquetas personalizadas
pca_data <- pca_data %>%
  left_join(cluster_sizes, by = "cluster")

# Graficar PCA con leyenda personalizada
ggplot(pca_data, aes(x = PC1, y = PC2, color = cluster_label)) +
  geom_point(size = 3, alpha = 0.7) +  # Puntos para los barrios
  theme_minimal() +
  labs(
    title = "Clusters Visualizados en el Espacio PCA",
    x = "PC1",
    y = "PC2",
    color = "Cluster"
  ) +
  theme(legend.position = "bottom")

NA
NA
delitos_df <- as.data.frame(delitos_matrix) %>%
  rownames_to_column(var = "barrio")  # Convertir rownames en columna llamada 'barrio'
resumen_clusters <- clusters %>%
  left_join(delitos_df, by = "barrio") %>%  # 'barrio' ya es una columna explícita
  group_by(cluster) %>%
  summarise_all(mean, na.rm = TRUE)
Warning: There were 2 warnings in `summarise()`.
The first warning was:
ℹ In argument: `barrio = (function (x, ...) ...`.
ℹ In group 1: `cluster = 1`.
Caused by warning in `mean.default()`:
! argument is not numeric or logical: returning NA
ℹ Run ]8;;ide:run:dplyr::last_dplyr_warnings()dplyr::last_dplyr_warnings()]8;; to see the 1 remaining warning.
print(resumen_clusters)
NA

Aplicamos silhouette para validar los clusters

library(cluster)

# Calcular índice de Silhouette
silhouette_result <- silhouette(kmeans_result$cluster, dist(delitos_matrix_normalized))

# Resumen del índice de Silhouette
summary(silhouette_result)
Silhouette of 48 units in 2 clusters from silhouette.default(x = kmeans_result$cluster, dist = dist(delitos_matrix_normalized)) :
 Cluster sizes and average silhouette widths:
       38        10 
0.6097475 0.2389282 
Individual silhouette widths:
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
-0.07585  0.40748  0.61745  0.53249  0.70691  0.72233 
# Inspeccionar los valores del índice de Silhouette
head(silhouette_result[, 1:3])  # Cluster, vecino más cercano, valor de Silhouette
     cluster neighbor   sil_width
[1,]       1        2  0.70668284
[2,]       2        1  0.16010046
[3,]       2        1  0.42544455
[4,]       2        1  0.07900193
[5,]       2        1 -0.07585089
[6,]       1        2  0.61609869
# Promedio del índice
mean(silhouette_result[, 3])
[1] 0.5324934
# Visualizar índice de Silhouette
fviz_silhouette(silhouette_result) +
  labs(title = "Índice de Silhouette por Cluster",
       x = "Índice de Silhouette",
       y = "Barrios")
NA

b) CON FDA: Creamos las curvas por barrio a lo largo del 2023 con FDA.


# Paso 2: Representar Datos como Curvas Funcionales con Suavizado

# Crear una base B-spline penalizada para suavizar las funciones
nbasis <- 25  # Ajusta el número de bases para mayor flexibilidad
lambda <- 1e-2  # Parámetro de suavizado; menor valor = más suave

# Crear la base B-spline
basis <- create.bspline.basis(rangeval = range(fechas_date), nbasis = nbasis)

# Definir el objeto de parámetros funcionales con suavizado
fd_par <- fdPar(basis, Lfdobj = int2Lfd(2), lambda = lambda)

# Convertir las filas de la matriz en funciones continuas suavizadas
fd_obj_suavizado <- smooth.basis(fechas_date, t(delitos_matrix), fd_par)$fd

# Visualizar las funciones suavizadas por barrio antes del clustering
colores <- rainbow(nrow(delitos_matrix))  # Generar colores únicos para cada barrio

# Crear el gráfico con dimensiones ampliadas
png("grafico_barrios_con_fda.png", width = 2000, height = 1200, res = 150)  # Resolución más alta

# Ajustar márgenes: más espacio derecho para la leyenda
par(mar = c(10, 4, 4, 35) + 0.1)  # Aumentar margen derecho para que la leyenda no quede cortada

# Graficar las funciones suavizadas
plot(fd_obj_suavizado, main = "Curvas por Barrio suavizadas con FDA",
     xlab = "Fecha", ylab = "Frecuencia",
     col = colores,
     lwd = 2, xaxt = "n")  # Desactivar eje X predeterminado
[1] "done"
# Crear etiquetas de fechas en el eje X
fechas_etiquetas <- seq(min(fechas_date), max(fechas_date), by = "2 weeks")  # Mostrar cada dos semanas
axis(1,
     at = as.numeric(fechas_etiquetas),  # Usar fechas reales como índices
     labels = format(fechas_etiquetas, "%d %b"),  # Día y mes
     las = 2,  # Rotar las etiquetas
     cex.axis = 0.8)  # Tamaño del texto en el eje X

# Crear una leyenda ajustada en el lado derecho del gráfico
legend("topleft",
       inset = c(1.1, 0),  # Reducir el espacio hacia la derecha
       legend = rownames(delitos_matrix),
       col = colores,
       lty = 1,
       lwd = 4,  # Grosor de las líneas
       ncol = 2,  # Dividir la leyenda en dos columnas
       cex = 0.9,  # Tamaño del texto ajustado
       xpd = TRUE)  # Permitir que la leyenda esté fuera del área de dibujo


# Guardar y cerrar el archivo PNG
dev.off()
null device 
          1 
# Verificar que el archivo fue creado
browseURL("grafico_barrios_con_fda.png")

# Paso 3: Clustering Funcional con K-Means
# Determinar el número óptimo de clusters con el método del codo
set.seed(123)
wss <- sapply(2:10, function(k) {
  kmeans(fd_obj_suavizado$coefs, centers = k, nstart = 20)$tot.withinss
})
Curvas por Barrio suavizada con FDA)
Curvas por Barrio suavizada con FDA)

Graficamos el método de codo y silhouette para definir el número de clusters

# Graficar el método del codo
plot(2:10, wss, type = "b", pch = 19, frame = FALSE,
     xlab = "Número de Clusters", ylab = "Suma de Errores al Cuadrado (WSS)",
     main = "Método del Codo para Clustering Funcional")


# Elegir un número de clusters (por ejemplo, 3)
num_clusters <- 2
kmeans_result <- kmeans(fd_obj_suavizado$coefs, centers = num_clusters, nstart = 20)

fviz_nbclust(fd_obj_suavizado$coefs, kmeans, method = "silhouette") +
  labs(title = "Índice de Silhouette")

Definimos los clusters correspondientes


fd_transposed <- t(fd_obj_suavizado$coefs)

# Aplicar kmeans funcional con manejo de clusters pequeños
num_clusters <- 2  # Cambiar según necesidad

# Aplicar clustering funcional
kmeans_result <- kmeans(t(fd_obj_suavizado$coefs), centers = num_clusters, nstart = 20)

Graficamos los clusters


# Asignar clusters a los barrios
# Crear el dataframe con los barrios y los clusters asignados
clusters <- data.frame(
  barrio = colnames(fd_obj_suavizado$coefs),  # Nombres de los barrios
  cluster = kmeans_result$cluster               # Clusters asignados
)

# Evaluar las curvas funcionales
matriz_evaluada <- t(eval.fd(fechas_date, fd_obj_suavizado))  # Evaluar en fechas_date

# Asignar las fechas como nombres de las columnas
colnames(matriz_evaluada) <- as.character(fechas_date)  # Convertir fechas a texto


# Actualizar etiquetas de los clusters con los tamaños
df_curves <- as.data.frame(matriz_evaluada) %>%
  rownames_to_column("barrio") %>%
  pivot_longer(-barrio, names_to = "fecha", values_to = "frecuencia") %>%
  mutate(
    fecha = as.Date(fecha),  # Convertir texto a objeto Date
    cluster = factor(clusters$cluster[match(barrio, clusters$barrio)])
  )


# Calcular cantidad de barrios únicos por cluster
cluster_sizes <- df_curves %>%
  group_by(cluster) %>%
  summarise(n_barrios = n_distinct(barrio))  # Contar barrios únicos

df_curves <- df_curves %>%
  left_join(cluster_sizes, by = "cluster") %>%  # Agregar tamaños al dataframe
  mutate(cluster_label = paste0("Cluster ", cluster, " (n=", n_barrios, ")"))  # Etiquetas personalizadas


ggplot(df_curves, aes(x = fecha, y = frecuencia, group = barrio, color = cluster_label)) +
  geom_line(alpha = 0.7) +
  theme_minimal() +
  labs(
    title = "Clustering Funcional por Barrio",
    x = "Fecha",
    y = "Frecuencia",
    color = "Cluster"
  ) +
  theme(legend.position = "bottom")



# Crear la tabla con los barrios por cluster
tabla_barrios <- df_curves %>%
  dplyr::select(barrio, cluster_label) %>%  # Seleccionar columnas necesarias
  distinct() %>%                     # Eliminar duplicados
  arrange(cluster_label, barrio)     # Ordenar por cluster y barrio

print(tabla_barrios)
NA

Graficamos las curvas promedios por cluster

# Calcular la frecuencia promedio por cluster y fecha
df_avg <- df_curves %>%
  group_by(cluster_label, fecha) %>%
  summarise(frecuencia_promedio = mean(frecuencia), .groups = "drop")

ggplot() +
  # Curvas individuales
  geom_line(data = df_curves, aes(x = fecha, y = frecuencia, group = barrio, color = cluster_label), alpha = 0.4) +
  # Curvas promedio por cluster
  geom_line(data = df_avg, aes(x = fecha, y = frecuencia_promedio, group = cluster_label, color = cluster_label), size = 1.5) +
  theme_minimal() +
  labs(
    title = "Clustering Funcional por Barrio con Curvas Promedio",
    x = "Fecha",
    y = "Frecuencia",
    color = "Cluster"
  ) +
  theme(legend.position = "bottom")

Validación de clusters con Silhouette

if (!require("cluster")) install.packages("cluster")
if (!require("factoextra")) install.packages("factoextra")
library(cluster)
library(factoextra)

# Asegurar que los clusters coinciden con la matriz
clusters <- df_curves %>%
  distinct(barrio, cluster) %>%
  arrange(barrio)  # Ordenar por barrio para garantizar coincidencia

# Calcular la matriz de distancias
matriz_clustering <- matriz_clustering[order(rownames(matriz_evaluada)), ]  # Ordenar filas por nombre de barrio
Error: object 'matriz_clustering' not found

c) PCA Funcional: También aplicamos PCA a los coeficientes funcionales y luego calculamos los clusters


# Paso 2: Extraer los coeficientes funcionales
coeficientes <- t(fd_obj_suavizado$coefs)

# Paso 3: Aplicar PCA a los coeficientes funcionales
pca_result <- prcomp(coeficientes, center = TRUE, scale. = TRUE)  # Normalizar para PCA

# Explorar la varianza explicada por las componentes principales
varianza_explicada <- cumsum(pca_result$sdev^2) / sum(pca_result$sdev^2) * 100
print(varianza_explicada)  # Ver proporción de varianza explicada
 [1]  87.95647  91.38211  93.35515  94.71343  95.90995  96.62760  97.19225  97.62889  98.04136  98.42655  98.74606
[12]  98.99938  99.19263  99.34640  99.49113  99.61475  99.71369  99.78494  99.84267  99.88468  99.92142  99.94973
[23]  99.96856  99.98455 100.00000
# Elegir el número de componentes principales
num_componentes <- 2  # Cambiar según necesidad (e.g., varianza explicada > 80%)
scores_pca <- pca_result$x[, 1:num_componentes]  # Tomar los scores de las componentes seleccionadas


# Paso 4: Aplicar k-means usando los scores de PCA
set.seed(123)  # Para reproducibilidad
num_clusters <- 2  # Cambiar según necesidad
kmeans_result <- kmeans(scores_pca, centers = num_clusters, nstart = 20)

# Ver los clusters asignados
clusters <- data.frame(
  barrio = colnames(fd_obj_suavizado$coefs),
  cluster = kmeans_result$cluster
)

# Ordenar el dataframe por cluster
clusters_ordenados <- clusters %>%
  arrange(cluster)


# Ver los primeros resultados
print(clusters_ordenados)

library(ggplot2)

# Crear un dataframe con las dos primeras componentes principales y los clusters
df_pca <- as.data.frame(scores_pca)  # Convertir los scores seleccionados a dataframe
df_pca$cluster <- as.factor(kmeans_result$cluster)  # Agregar los clusters asignados
df_pca$barrio <- colnames(fd_obj_suavizado$coefs)  # Agregar nombres de los barrios

library(ggplot2)
library(dplyr)

# Calcular la cantidad de barrios por cluster
cluster_sizes <- df_pca %>%
  group_by(cluster) %>%
  summarise(n_barrios = n())  # Contar barrios por cluster

# Crear etiquetas personalizadas para la leyenda
cluster_labels <- cluster_sizes %>%
  mutate(cluster_label = paste0("Cluster ", cluster, " (n=", n_barrios, ")")) %>%
  pull(cluster_label)  # Extraer etiquetas como vector

# Mapear las etiquetas a los clusters
names(cluster_labels) <- cluster_sizes$cluster  # Asignar etiquetas por número de cluster

# Graficar los clusters en el espacio PCA con las etiquetas personalizadas
ggplot(df_pca, aes(x = PC1, y = PC2, color = cluster)) +
  geom_point(size = 3, alpha = 0.7) +  # Representar los puntos
  theme_minimal() +
  labs(
    title = "Clusters Visualizados en el Espacio PCA",
    x = "Componente Principal 1",
    y = "Componente Principal 2",
    color = "Cluster"
  ) +
  theme(legend.position = "bottom") +
  scale_color_manual(
    values = c("#1b9e77", "#d95f02", "#7570b3", "#e7298a"),  # Colores personalizados
    labels = cluster_labels  # Etiquetas con la cantidad de barrios
  )

NA
NA

Validación de clusters nuevamente con Silhouette

library(cluster)
library(factoextra)

# Calcular la matriz de distancias usando los scores PCA
dist_pca <- dist(df_pca[, c("PC1", "PC2")])

# Calcular el índice de Silhouette
silhouette_result <- silhouette(as.numeric(df_pca$cluster), dist_pca)

# Ver un resumen del índice de Silhouette
summary(silhouette_result)
Silhouette of 48 units in 2 clusters from silhouette.default(x = as.numeric(df_pca$cluster), dist = dist_pca) :
 Cluster sizes and average silhouette widths:
       38        10 
0.6837485 0.4056394 
Individual silhouette widths:
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
0.02632 0.53374 0.74402 0.62581 0.77813 0.79962 
# Promedio del índice de Silhouette
silhouette_avg <- mean(silhouette_result[, 3])
print(paste("Índice de Silhouette promedio:", round(silhouette_avg, 3)))
[1] "Índice de Silhouette promedio: 0.626"
# Visualizar el índice de Silhouette
fviz_silhouette(silhouette_result) +
  labs(
    title = "Índice de Silhouette para Clusters en PCA",
    x = "Índice de Silhouette",
    y = "Barrios"
  ) +
  theme_minimal()
NA

LS0tCnRpdGxlOiAiVHJhYmFqbyBQcsOhY3RpY28gZmluYWwgRUVBLTIwMjQiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAyCiAgICB0b2NfZmxvYXQ6IHRydWUKLS0tCgpgYGB7cn0KIyBJbnN0YWxhciBlbCBwYXF1ZXRlIHNpIG5vIGVzdMOhIGluc3RhbGFkbwppZiAoIXJlcXVpcmUoIm9wZW54bHN4IikpIGluc3RhbGwucGFja2FnZXMoIm9wZW54bHN4IikKaWYgKCFyZXF1aXJlKCJkcGx5ciIpKSBpbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpCiMgQ2FyZ2FyIGVsIHBhcXVldGUKbGlicmFyeShkcGx5cikKYGBgCgojIDEpIEZ1ZW50ZSBkZSBkYXRvcwoKIyMgQ2FyZ2EgZWwgZGF0YXNldAoKCmBgYHtyfQpsaWJyYXJ5KG9wZW54bHN4KQpkZWxpdG9zIDwtIHJlYWQueGxzeCgiL1VzZXJzL2JxdWlub25lei9Eb3dubG9hZHMvZGVsaXRvc18yMDIzLnhsc3giKQpkZWxpdG9zCmBgYAoKYGBge3J9CnN1bW1hcnkoZGVsaXRvcykKCmBgYAoKYGBge3J9CmRpbShkZWxpdG9zKQpgYGAKCkVsIGRhdGFzZXQgYW50ZXMgZGVsIHByZXByb2Nlc2FtaWVudG8gY3VlbnRhIGNvbiAxNSBjb2x1bW5hcyB5IDE1Ny40NjEgcmVnaXN0cm9zIGNvcnJlc3BvbmRpZW50ZXMgYWwgYcOxbyAyMDIzLgoKCgojIDIpIFByZXByb2Nlc2FtaWVudG8KCiMjIFZlcmlmaWNhY2nDs24gZGUgbnVsb3MKCmBgYHtyfQpjb2xTdW1zKGlzLm5hKGRlbGl0b3MpKQpgYGAKClRpcG9zIGRlIERlbGl0b3MKCmBgYHtyfQpjYW50aWRhZF92YWxvcmVzX3VuaWNvcyA8LSAodW5pcXVlKGRlbGl0b3MkdGlwbykpCnByaW50KGNhbnRpZGFkX3ZhbG9yZXNfdW5pY29zKQpgYGAKClN1YnRpcG9zIGRlIGRlbGl0b3MKCmBgYHtyfQpjb250ZW9fcG9yX3RpcG8gPC0gdGFibGUoZGVsaXRvcyR0aXBvKQpwcmludChjb250ZW9fcG9yX3RpcG8pCmBgYAoKYGBge3J9CmNhbnRpZGFkX3ZhbG9yZXNfdW5pY29zIDwtICh1bmlxdWUoZGVsaXRvcyRzdWJ0aXBvKSkKcHJpbnQoY2FudGlkYWRfdmFsb3Jlc191bmljb3MpCmNvbnRlb19wb3Jfc3VidGlwbyA8LSB0YWJsZShkZWxpdG9zJHN1YnRpcG8pCnByaW50KGNvbnRlb19wb3Jfc3VidGlwbykKYGBgCgojIyBDcmVhY2nDs24gZGUgdmFyaWFibGVzCgpBZ3JlZ2Ftb3MgbGEgY29sdW1uYSBkZSBmcmFuamEgaG9yYXJpYQoKYGBge3J9CmxpYnJhcnkoZHBseXIpCiNHZW5lcm8gbGEgY29sdW1uYSBmcmFuamEoaG9yYXJpYSkKZGVsaXRvcyA8LSBkZWxpdG9zICU+JQogIG11dGF0ZSgKICAgIGZyYW5qYV9ob3JhcmlhID0gY2FzZV93aGVuKAogICAgICBmcmFuamEgPj0gNiAmIGZyYW5qYSA8IDEyIH4gIk1hw7FhbmEiLAogICAgICBmcmFuamEgPj0gMTIgJiBmcmFuamEgPCAxOCB+ICJUYXJkZSIsCiAgICAgIGZyYW5qYSA+PSAxOCAmIGZyYW5qYSA8IDI0IH4gIk5vY2hlIiwKICAgICAgZnJhbmphID49IDAgJiBmcmFuamEgPCA2ICB+ICJNYWRydWdhZGEiLAogICAgICBUUlVFIH4gIkRlc2Nvbm9jaWRvIiAjIFBhcmEgY2Fzb3MgZnVlcmEgZGUgcmFuZ28gCiAgICApCiAgKQpkZWxpdG9zCmBgYAoKQWdyZWdhbW9zIHVuYSBjb2x1bW5hIHF1ZSBpbmRpY2Egc2kgZXMgZmluIGRlIHNlbWFuYSBvIGTDrWEgZGUgc2VtYW5hCgpgYGB7cn0KZGVsaXRvcyR0aXBvX2RpYSA8LSBpZmVsc2UoZGVsaXRvcyRkaWEgJWluJSBjKCJTQUIiLCAiRE9NIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJGaW4gZGUgc2VtYW5hIiwgIkTDrWEgZGUgc2VtYW5hIikKZGVsaXRvcwpgYGAKCkFncmVnYW1vcyB1bmEgY29sdW1uYSBxdWUgaW5kaWNhIGxhIGVzdGFjacOzbiBkZWwgYcOxbwoKYGBge3J9CiMgQ3JlYXIgY29sdW1uYSAnZXN0YWNpb24nIGJhc2FkYSBlbiBlbCBtZXMKZGVsaXRvcyA8LSBkZWxpdG9zICU+JQogIG11dGF0ZSgKICAgIGVzdGFjaW9uID0gY2FzZV93aGVuKAogICAgICBtZXMgJWluJSBjKCJkaWNpZW1icmUiLCAiZW5lcm8iLCAiZmVicmVybyIpIH4gIlZlcmFubyIsCiAgICAgIG1lcyAlaW4lIGMoIm1hcnpvIiwgImFicmlsIiwgIm1heW8iKSAgICAgICB+ICJPdG/DsW8iLAogICAgICBtZXMgJWluJSBjKCJqdW5pbyIsICJqdWxpbyIsICJhZ29zdG8iKSAgICAgfiAiSW52aWVybm8iLAogICAgICBtZXMgJWluJSBjKCJzZXB0aWVtYnJlIiwgIm9jdHVicmUiLCAibm92aWVtYnJlIikgfiAiUHJpbWF2ZXJhIiwKICAgICAgVFJVRSB+ICJEZXNjb25vY2lkbyIgIyBQYXJhIGNhc29zIG5vIGVzcGVyYWRvcwogICAgKQogICkKZGVsaXRvcwpgYGAKCkNvbnZlcnNpw7NuIGRlIGxvbmdpdHVkIHkgbGF0aXR1ZAoKYGBge3J9CiMgQ29udmVydGlyIGxvbmdpdHVkIHkgbGF0aXR1ZCBhIG51bcOpcmljYXMgeSBlc2NhbGFybGFzIGNvcnJlY3RhbWVudGUKZGVsaXRvcyA8LSBkZWxpdG9zICU+JQogIG11dGF0ZSgKICAgIGxvbmdpdHVkID0gYXMubnVtZXJpYyhsb25naXR1ZCkgLyAxZTYsICAjIEVzY2FsYXIgZGl2aWRpZW5kbyBlbnRyZSAxMF42CiAgICBsYXRpdHVkID0gYXMubnVtZXJpYyhsYXRpdHVkKSAvIDFlNiAgICAjIEVzY2FsYXIgZGl2aWRpZW5kbyBlbnRyZSAxMF42CiAgKQoKIyBWZXJpZmljYXIgbGFzIHByaW1lcmFzIGZpbGFzIHBhcmEgY29uZmlybWFyCmhlYWQoZGVsaXRvc1tjKCJsb25naXR1ZCIsICJsYXRpdHVkIildKQpgYGAKCkVsaW1pbmFjacOzbiBkZSBudWxvcyB5IGJhcnJpb3MgcXVlIG5vIGZvcm1hbiBwYXJ0ZSBkZSBDQUJBCgpgYGB7cn0KbGlicmFyeShzdHJpbmdyKQpkZWxpdG9zIDwtIG5hLm9taXQoZGVsaXRvcykKZGltKGRlbGl0b3MpCgpkZWxpdG9zIDwtIGRlbGl0b3MgJT4lIAogIGZpbHRlcihiYXJyaW8gIT0gIjAiLCBiYXJyaW8gIT0gIlNpbiBnZW8iLCBiYXJyaW8gIT0gIk5VTEwiLCBiYXJyaW8gIT0gIk5PIEVTUEVDSUZJQ0FEQSIsIGJhcnJpbyAhPSAiR1JFR09SSU8gREUgTEFGRVJSRVJFIikgJT4lCiAgbXV0YXRlKGJhcnJpbyA9IHN0cl9yZXBsYWNlX2FsbChiYXJyaW8sICJOVU5FWiIsICJOVcORRVoiKSkKCmBgYAoKTHVlZ28gZGUgbGEgbGltcGllemEgcXVlZGFyb24gdW4gdG90YWwgZGUgMTU0NTIxIHkgMTggYXRyaWJ1dG9zLCB5YSBxdWUgYXJyZWdsYW1vcyAzIHZhcmlhYmxlcyBhZGljaW9uYWxlczogZnJhbmphX2hvcmFyaWEsIGRpYSB5IGVzdGFjaW9uCgojIyBHcsOhZmljb3MgZXhwbG9yYXRvcmlvcwoKIyMjIERpc3RyaWJ1Y2nDs24gZGUgRGVsaXRvcyBwb3IgVGlwbyB5IE1lcwpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyKQoKIyBBZ3J1cGFyIGxvcyBkYXRvcwpkZWxpdG9zX2FncnVwYWRvc190aXBvIDwtIGRlbGl0b3MgJT4lCiAgZ3JvdXBfYnkobWVzLCB0aXBvKSAlPiUKICBzdW1tYXJpc2UodG90YWwgPSBuKCksIC5ncm91cHMgPSAiZHJvcCIpCgojIENyZWFyIGVsIGdyw6FmaWNvIHNpbiBuw7ptZXJvcwpncmFmaWNvX2FwaWxhZG8gPC0gZ2dwbG90KGRlbGl0b3NfYWdydXBhZG9zX3RpcG8sIGFlcyh4ID0gbWVzLCB5ID0gdG90YWwsIGZpbGwgPSB0aXBvKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJzdGFjayIpICsKICBmYWNldF93cmFwKH4gdGlwbywgc2NhbGVzID0gImZyZWVfeSIpICsgICMgRGl2aWRpciBwb3IgdGlwbyBkZSBkZWxpdG8KICBsYWJzKAogICAgdGl0bGUgPSAiRGlzdHJpYnVjacOzbiBkZSBEZWxpdG9zIHBvciBUaXBvIHkgTWVzIiwKICAgIHggPSAiTWVzIiwKICAgIHkgPSAiTsO6bWVybyBkZSBEZWxpdG9zIgogICkgKwogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTQpICsKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgICMgVGFtYcOxbyBkZWwgdMOtdHVsbyBkZSBjYWRhIGZhY2V0YQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksICAjIENlbnRyYXIgZWwgdMOtdHVsbwogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwgICMgQWp1c3RhciB0YW1hw7FvIGRlIHRleHRvIGRlIGxhIGxleWVuZGEKICAgIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC44LCAiY20iKSAgIyBUYW1hw7FvIGRlIGxhcyBjYWphcyBkZSBsYSBsZXllbmRhCiAgKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQzIikgICMgUGFsZXRhIGRlIGNvbG9yZXMgZGlmZXJlbmNpYWRvcwoKIyBNb3N0cmFyIGVsIGdyw6FmaWNvCnByaW50KGdyYWZpY29fYXBpbGFkbykKCmBgYAoKIyMjIERpc3RyaWJ1Y2nDs24gZGUgRGVsaXRvcyBwb3IgVGlwbyB5IEZyYW5qYSBIb3JhcmlhCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyKQoKIyBBZ3J1cGFyIGxvcyBkYXRvcyBwb3IgZnJhbmphIGhvcmFyaWEsIHRpcG8geSBzdWJ0aXBvCmRlbGl0b3NfYWdydXBhZG9zX2hvcmFyaW8gPC0gZGVsaXRvcyAlPiUKICBncm91cF9ieShmcmFuamEsIHRpcG8pICU+JQogIHN1bW1hcmlzZSh0b3RhbCA9IG4oKSwgLmdyb3VwcyA9ICJkcm9wIikKCiMgQ3JlYXIgZWwgZ3LDoWZpY28gYXBpbGFkbyBwb3IgZnJhbmphIGhvcmFyaWEKZ3JhZmljb19ob3JhcmlvcyA8LSBnZ3Bsb3QoZGVsaXRvc19hZ3J1cGFkb3NfaG9yYXJpbywgYWVzKHggPSBmcmFuamEsIHkgPSB0b3RhbCwgZmlsbCA9IHRpcG8pKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gInN0YWNrIikgKwogIGZhY2V0X3dyYXAofiB0aXBvLCBzY2FsZXMgPSAiZnJlZV95IikgKyAgIyBEaXZpZGlyIHBvciB0aXBvIGRlIGRlbGl0bwogIGxhYnMoCiAgICB0aXRsZSA9ICJEaXN0cmlidWNpw7NuIGRlIERlbGl0b3MgcG9yIFRpcG8geSBGcmFuamEgSG9yYXJpYSIsCiAgICB4ID0gIkZyYW5qYSBIb3JhcmlhIiwKICAgIHkgPSAiTsO6bWVybyBkZSBEZWxpdG9zIgogICkgKwogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTQpICsKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgICMgVGFtYcOxbyBkZWwgdMOtdHVsbyBkZSBjYWRhIGZhY2V0YQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksICAjIENlbnRyYXIgZWwgdMOtdHVsbwogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwgICMgQWp1c3RhciB0YW1hw7FvIGRlIHRleHRvIGRlIGxhIGxleWVuZGEKICAgIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC44LCAiY20iKSAgIyBUYW1hw7FvIGRlIGxhcyBjYWphcyBkZSBsYSBsZXllbmRhCiAgKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQzIikgICMgUGFsZXRhIGRlIGNvbG9yZXMgZGlmZXJlbmNpYWRvcwoKIyBNb3N0cmFyIGVsIGdyw6FmaWNvCnByaW50KGdyYWZpY29faG9yYXJpb3MpCgpgYGAKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShkcGx5cikKCiMgQWdydXBhciBsb3MgZGF0b3MgcG9yIGZyYW5qYSBob3JhcmlhCmRlbGl0b3NfcG9yX2hvcmEgPC0gZGVsaXRvcyAlPiUKICBncm91cF9ieShmcmFuamEpICU+JQogIHN1bW1hcmlzZSh0b3RhbCA9IG4oKSwgLmdyb3VwcyA9ICJkcm9wIikKCiMgQ3JlYXIgZWwgZ3LDoWZpY28gZGUgYmFycmFzCmdyYWZpY29faG9yYXMgPC0gZ2dwbG90KGRlbGl0b3NfcG9yX2hvcmEsIGFlcyh4ID0gZnJhbmphLCB5ID0gdG90YWwpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAic3RlZWxibHVlIiwgY29sb3IgPSAiYmxhY2siLCB3aWR0aCA9IDAuNykgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJEaXN0cmlidWNpw7NuIGRlIERlbGl0b3MgcG9yIEhvcmEiLAogICAgeCA9ICJIb3JhIGRlbCBEw61hIiwKICAgIHkgPSAiTsO6bWVybyBkZSBEZWxpdG9zIgogICkgKwogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTQpICsKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoc2l6ZSA9IDAuNSwgbGluZXR5cGUgPSAiZG90dGVkIiksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpCiAgKQoKIyBNb3N0cmFyIGVsIGdyw6FmaWNvCnByaW50KGdyYWZpY29faG9yYXMpCgpgYGAKCgoKIyMgVHJhbnNmb3JtYWNpw7NuIGRlIHZhcmlhYmxlcyBjYXRlZ8OzcmljYXMKClRyYW5zZm9ybWFtb3MgdG9kb3MgbG9zIGNhcmFjdGVyZXMgYSBudW3DqXJpY29zLCBleGNlcHR1YW5kbyBtb3RvLGFybWEsbGF0LGxvbmcKCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQoKIyBDb252ZXJ0aXIgY29sdW1uYXMgZXNwZWPDrWZpY2FzIGB1c29fYXJtYWAgeSBgdXNvX21vdG9gIChTw609MSwgTm89MCkKZGVsIDwtIGRlbGl0b3MgJT4lCiAgbXV0YXRlKAogICAgdXNvX2FybWEgPSBpZmVsc2UodXNvX2FybWEgPT0gIlNJIiwgMSwgMCksCiAgICB1c29fbW90byA9IGlmZWxzZSh1c29fbW90byA9PSAiU0kiLCAxLCAwKQogICkKCiMgQ29udmVydGlyIG90cmFzIGNvbHVtbmFzIGNhdGVnw7NyaWNhcyBhIG51ZXZhcyBjb2x1bW5hcyBjb2RpZmljYWRhcwpkZWwgPC0gZGVsICU+JQogIG11dGF0ZShhY3Jvc3MoCiAgICAuY29scyA9IHdoZXJlKGlzLmNoYXJhY3RlcikgJiAhYWxsX29mKGMoImxvbmdpdHVkIiwgImxhdGl0dWQiLCAidXNvX2FybWEiLCAidXNvX21vdG8iKSksIAogICAgLmZucyA9IH4gYXMubnVtZXJpYyhhcy5mYWN0b3IoLikpLAogICAgLm5hbWVzID0gInsuY29sfV9jb2QiCiAgKSkgJT4lCiAgbXV0YXRlKGFjcm9zcygKICAgIC5jb2xzID0gd2hlcmUoaXMuZmFjdG9yKSAmICFhbGxfb2YoYygibG9uZ2l0dWQiLCAibGF0aXR1ZCIsICJ1c29fYXJtYSIsICJ1c29fbW90byIpKSwgCiAgICAuZm5zID0gfiBhcy5udW1lcmljKC4pLAogICAgLm5hbWVzID0gInsuY29sfV9jb2QiCiAgKSkKCiMgVmVyaWZpY2FyIGxhIGVzdHJ1Y3R1cmEgZGVsIGRhdGFzZXQgZGVzcHXDqXMgZGUgbGFzIHRyYW5zZm9ybWFjaW9uZXMKc3RyKGRlbCkKCmBgYAoKIyMjIEV2b2x1Y2nDs24gVGVtcG9yYWwgZGUgRGVsaXRvcyBwb3IgVGlwbwoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShkcGx5cikKCiMgQWdydXBhY2nDs24gcG9yIGHDsW8sIG1lcyB5IHRpcG8gZGUgZGVsaXRvCmRlbGl0b3NfdGVtcG9yYWwgPC0gZGVsICU+JQogIGdyb3VwX2J5KGFuaW8sIG1lc19jb2QsIHRpcG8pICU+JQogIHN1bW1hcmlzZSh0b3RhbF9kZWxpdG9zID0gbigpLCAuZ3JvdXBzID0gImRyb3AiKQoKIyBWaXN1YWxpemFjacOzbiBkZSBsYSBldm9sdWNpw7NuCmdncGxvdChkZWxpdG9zX3RlbXBvcmFsLCBhZXMoeCA9IG1lc19jb2QsIHkgPSB0b3RhbF9kZWxpdG9zLCBjb2xvciA9IHRpcG8sIGdyb3VwID0gdGlwbykpICsKICBnZW9tX2xpbmUoc2l6ZSA9IDEpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gMToxMiwgbGFiZWxzID0gbW9udGguYWJiKSArCiAgbGFicyh0aXRsZSA9ICJFdm9sdWNpw7NuIFRlbXBvcmFsIGRlIERlbGl0b3MgcG9yIFRpcG8gcG9yIG1lcyIsCiAgICAgICB4ID0gIk1lcyIsCiAgICAgICB5ID0gIk7Dum1lcm8gZGUgRGVsaXRvcyIsCiAgICAgICBjb2xvciA9ICJUaXBvIGRlIERlbGl0byIpICsKICB0aGVtZV9taW5pbWFsKCkKCmBgYAoKIyMjIEV2b2x1Y2nDs24gVGVtcG9yYWwgZGUgRGVsaXRvcyBwb3IgVGlwbwpgYGB7cn0KZGVsaXRvc190ZW1wb3JhbF9oIDwtIGRlbCAlPiUKICBncm91cF9ieShmcmFuamEsIHRpcG8pICU+JQogIHN1bW1hcmlzZSh0b3RhbF9kZWxpdG9zID0gbigpLCAuZ3JvdXBzID0gImRyb3AiKQoKZGVsaXRvc190ZW1wb3JhbF9oJGZyYW5qYSA8LSBhcy5jaGFyYWN0ZXIoZGVsaXRvc190ZW1wb3JhbF9oJGZyYW5qYSkKCgpnZ3Bsb3QoZGVsaXRvc190ZW1wb3JhbF9oLCBhZXMoeCA9IGZyYW5qYSwgeSA9IHRvdGFsX2RlbGl0b3MsIGNvbG9yID0gdGlwbywgZ3JvdXAgPSB0aXBvKSkgKwogIGdlb21fbGluZShzaXplID0gMSkgKwogIGxhYnModGl0bGUgPSAiRXZvbHVjacOzbiBUZW1wb3JhbCBkZSBEZWxpdG9zIHBvciBUaXBvIHkgZnJhbmphIGhvcmFyaWEiLAogICAgICAgeCA9ICJGcmFuamEgSG9yYXJpYSIsCiAgICAgICB5ID0gIk7Dum1lcm8gZGUgRGVsaXRvcyIsCiAgICAgICBjb2xvciA9ICJUaXBvIGRlIERlbGl0byIpICsKICB0aGVtZV9taW5pbWFsKCkKCgpgYGAKCgojIyMgRnJlY3VlbmNpYSBkZSBEZWxpdG9zIHBvciBCYXJyaW8geSBUaXBvIApgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyKQoKIyBBZ3J1cGFyIGxvcyBkYXRvcyBwb3IgYmFycmlvIHkgY2FsY3VsYXIgZWwgdG90YWwgZGUgZGVsaXRvcyBwb3IgYmFycmlvCmRlbGl0b3NfcG9yX2JhcnJpbyA8LSBkZWxpdG9zICU+JQogIGdyb3VwX2J5KGJhcnJpbykgJT4lCiAgc3VtbWFyaXNlKHRvdGFsX2RlbGl0b3MgPSBuKCksIC5ncm91cHMgPSAiZHJvcCIpICU+JQogIG11dGF0ZShiYXJyaW8gPSByZW9yZGVyKGJhcnJpbywgLXRvdGFsX2RlbGl0b3MpKSAgIyBPcmRlbmFyIGJhcnJpb3MgcG9yIG7Dum1lcm8gZGUgZGVsaXRvcwoKIyBGaWx0cmFyIGxvcyAxNSBiYXJyaW9zIGNvbiBtw6FzIGRlbGl0b3MKdG9wX2JhcnJpb3MgPC0gZGVsaXRvc19wb3JfYmFycmlvCgojIENyZWFyIGVsIGdyw6FmaWNvIGRlIGJhcnJhcwpnZ3Bsb3QodG9wX2JhcnJpb3MsIGFlcyh4ID0gYmFycmlvLCB5ID0gdG90YWxfZGVsaXRvcykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJzdGVlbGJsdWUiKSArICAjIENvbG9yIHPDs2xpZG8gcGFyYSBsYXMgYmFycmFzCiAgbGFicygKICAgIHRpdGxlID0gIkZyZWN1ZW5jaWEgVG90YWwgZGUgRGVsaXRvcyBwb3IgQmFycmlvIiwKICAgIHggPSAiQmFycmlvIiwKICAgIHkgPSAiTsO6bWVybyBkZSBEZWxpdG9zIgogICkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3QgPSAxKSwgICMgUm90YXIgZXRpcXVldGFzIGEgOTAgZ3JhZG9zCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSAgIyBDZW50cmFyIGVsIHTDrXR1bG8KICApCgojIEd1YXJkYXIgZWwgZ3LDoWZpY28gY29uIGRpbWVuc2lvbmVzIG1heW9yZXMKZ2dzYXZlKCJncmFmaWNvX2RlbGl0b3NfdG90YWxlc19iYXJyaW8ucG5nIiwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gOCwgZHBpID0gMzAwKQoKCmBgYAojIyMgRGlhIGRlIHNlbWFuYSB2cyBGaW5kZQpgYGB7cn0KIyBBZ3J1cGFyIGxvcyBkYXRvcyBwb3IgZMOtYSBkZSBsYSBzZW1hbmEKZGVsaXRvc19wb3JfZGlhIDwtIGRlbGl0b3MgJT4lCiAgZ3JvdXBfYnkodGlwb19kaWEpICU+JQogIHN1bW1hcmlzZSh0b3RhbCA9IG4oKSwgLmdyb3VwcyA9ICJkcm9wIikKCiMgQ3JlYXIgZWwgZ3LDoWZpY28gZGUgYmFycmFzCmdncGxvdChkZWxpdG9zX3Bvcl9kaWEsIGFlcyh4ID0gdGlwb19kaWEsIHkgPSB0b3RhbCwgZmlsbCA9IHRpcG9fZGlhKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDAuNykgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJGcmVjdWVuY2lhIGRlIERlbGl0b3MgcG9yIETDrWEgZGUgbGEgU2VtYW5hIiwKICAgIHggPSAiRMOtYSBkZSBsYSBTZW1hbmEiLAogICAgeSA9ICJOw7ptZXJvIGRlIERlbGl0b3MiCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZSgKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLCAgIyBDZW50cmFyIGVsIHTDrXR1bG8KICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkgICMgUm90YXIgZXRpcXVldGFzIHNpIGVzIG5lY2VzYXJpbwogICkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MyIpICAjIFBhbGV0YSBkZSBjb2xvcmVzCgoKYGBgCiMjIyBGcmVjdWVuY2lhIGRlIERlbGl0b3MgcG9yIEVzdGFjacOzbiBkZWwgQcOxbwpgYGB7cn0KIyBBZ3J1cGFyIGxvcyBkYXRvcyBwb3IgZXN0YWNpw7NuCmRlbGl0b3NfcG9yX2VzdGFjaW9uIDwtIGRlbGl0b3MgJT4lCiAgZ3JvdXBfYnkoZXN0YWNpb24pICU+JQogIHN1bW1hcmlzZSh0b3RhbCA9IG4oKSwgLmdyb3VwcyA9ICJkcm9wIikKCiMgQ3JlYXIgZWwgZ3LDoWZpY28gZGUgYmFycmFzCmdncGxvdChkZWxpdG9zX3Bvcl9lc3RhY2lvbiwgYWVzKHggPSBlc3RhY2lvbiwgeSA9IHRvdGFsLCBmaWxsID0gZXN0YWNpb24pKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBsYWJzKAogICAgdGl0bGUgPSAiRnJlY3VlbmNpYSBkZSBEZWxpdG9zIHBvciBFc3RhY2nDs24gZGVsIEHDsW8iLAogICAgeCA9ICJFc3RhY2nDs24iLAogICAgeSA9ICJOw7ptZXJvIGRlIERlbGl0b3MiCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpCgpgYGAKCgojIDMpIEFwbGljYWNpw7NuIGRlIEZEQQoKIyMgQSkgRnJlbmN1ZW5jaWEgZGUgZGVsaXRvcyBhZ3J1cGFkb3MgcG9yIGZyYW5qYSAoMjQpIHkgZmVjaGEKCmBgYHtyfQojIENhcmdhciBsaWJyZXLDrWFzIG5lY2VzYXJpYXMKaWYgKCFyZXF1aXJlKCJSQ29sb3JCcmV3ZXIiKSkgaW5zdGFsbC5wYWNrYWdlcygiUkNvbG9yQnJld2VyIikKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZmRhLnVzYykKbGlicmFyeShmZGEpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KHRpYmJsZSkgICMgUGFyYSB1c2FyIGNvbHVtbl90b19yb3duYW1lcwoKCiMgQWdydXBhciBwb3IgZMOtYSB5IGhvcmFyaW8sIHkgY29udGFyIGxhIGZyZWN1ZW5jaWEgZGUgZGVsaXRvcwpkZWxpdG9zX3Bvcl9kaWFfaG9yYXJpbyA8LSBkZWxpdG9zICU+JQogIGdyb3VwX2J5KGZyYW5qYSwgZmVjaGEpICU+JSAgIyAnZnJhbmphX2hvcmFyaWEnIHJlcHJlc2VudGEgbG9zIGhvcmFyaW9zCiAgc3VtbWFyaXNlKGZyZWN1ZW5jaWEgPSBzdW0oY2FudGlkYWQsIG5hLnJtID0gVFJVRSksIC5ncm91cHMgPSAiZHJvcCIpCgojIENyZWFyIHVuYSBtYXRyaXogY29uIGZpbGFzIGNvbW8gZMOtYXMgeSBjb2x1bW5hcyBjb21vIGZyYW5qYXMgaG9yYXJpYXMKZGVsaXRvc19mcmFuamFfbWF0cml4IDwtIGRlbGl0b3NfcG9yX2RpYV9ob3JhcmlvICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBmZWNoYSwgdmFsdWVzX2Zyb20gPSBmcmVjdWVuY2lhLCB2YWx1ZXNfZmlsbCA9IDApICU+JQogIGNvbHVtbl90b19yb3duYW1lcyh2YXIgPSAiZnJhbmphIikgJT4lCiAgYXMubWF0cml4KCkKCmRlbGl0b3NfZnJhbmphX21hdHJpeCA8LSBkZWxpdG9zX2ZyYW5qYV9tYXRyaXhbY29tcGxldGUuY2FzZXMoZGVsaXRvc19mcmFuamFfbWF0cml4KSwgXQoKYGBgCgojIyMgYSkgU0lOIEZEQTogQ3JlYW1vcyBsYXMgY3VydmFzIHBvciBmcmFuamEgYSBsbyBsYXJnbyBkZWwgMjAyMyBzaW4gRkRBLgoKYGBge3J9CgojIENyZWFyIHVuIHZlY3RvciBkZSBmZWNoYXMgcmVhbGVzCmZlY2hhc19kYXRlIDwtIHNlcS5EYXRlKGZyb20gPSBhcy5EYXRlKCIyMDIzLTAxLTAxIiksIGJ5ID0gImRheSIsIGxlbmd0aC5vdXQgPSBuY29sKGRlbGl0b3NfZnJhbmphX21hdHJpeCkpCgojIENyZWFyIGNvbG9yZXMgw7puaWNvcyBwYXJhIGNhZGEgYmFycmlvCmNvbG9yZXMgPC0gcmFpbmJvdyhucm93KGRlbGl0b3NfZnJhbmphX21hdHJpeCkpCgoKIyBDcmVhciBlbCBncsOhZmljbyBjb24gZGltZW5zaW9uZXMgYW1wbGlhZGFzCnBuZygiZ3JhZmljb19mcmFuamFzX3Npbl9mZGEucG5nIiwgd2lkdGggPSAyMDAwLCBoZWlnaHQgPSAxMjAwLCByZXMgPSAxNTApICAjIFJlc29sdWNpw7NuIG3DoXMgYWx0YQoKIyBBanVzdGFyIG3DoXJnZW5lczogbcOhcyBlc3BhY2lvIGRlcmVjaG8gcGFyYSBsYSBsZXllbmRhCnBhcihtYXIgPSBjKDEwLCAzNSwgNCwgNCkgKyAwLjEpICAjIEF1bWVudGFyIG1hcmdlbiBpenF1aWVyZG8KCiMgR3JhZmljYXIgbGFzIGN1cnZhcyBvcmlnaW5hbGVzCm1hdHBsb3QoZmVjaGFzX2RhdGUsIHQoZGVsaXRvc19mcmFuamFfbWF0cml4KSwgdHlwZSA9ICJsIiwgbHR5ID0gMSwgY29sID0gY29sb3JlcywgbHdkID0gMiwKICAgICAgICB4bGFiID0gIkZlY2hhIiwgeWxhYiA9ICJGcmVjdWVuY2lhIiwgeGF4dCA9ICJuIiwKICAgICAgICBtYWluID0gIkN1cnZhcyBwb3IgRnJhbmphIChTaW4gc3Vhdml6YWRvKSIpCgojIENyZWFyIGV0aXF1ZXRhcyBkZSBmZWNoYXMgZW4gZWwgZWplIFgKZmVjaGFzX2V0aXF1ZXRhcyA8LSBzZXEobWluKGZlY2hhc19kYXRlKSwgbWF4KGZlY2hhc19kYXRlKSwgYnkgPSAiMiB3ZWVrcyIpICAjIE1vc3RyYXIgY2FkYSBkb3Mgc2VtYW5hcwpheGlzKDEsCiAgICAgYXQgPSBhcy5udW1lcmljKGZlY2hhc19ldGlxdWV0YXMpLCAgIyBVc2FyIGZlY2hhcyByZWFsZXMgY29tbyDDrW5kaWNlcwogICAgIGxhYmVscyA9IGZvcm1hdChmZWNoYXNfZXRpcXVldGFzLCAiJWQgJWIiKSwgICMgRMOtYSB5IG1lcwogICAgIGxhcyA9IDIsICAjIFJvdGFyIGxhcyBldGlxdWV0YXMKICAgICBjZXguYXhpcyA9IDAuOCkgICMgVGFtYcOxbyBkZWwgdGV4dG8gZW4gZWwgZWplIFhwZQoKIyBDcmVhciB1bmEgbGV5ZW5kYSBtw6FzIGFtcGxpYSBlbiBlbCBtYXJnZW4gaXpxdWllcmRvCmxlZ2VuZCgidG9wcmlnaHQiLAogICAgICAgaW5zZXQgPSBjKDEuMiwgMCksICAgIyBNw6FzIGVzcGFjaW8gaGFjaWEgbGEgaXpxdWllcmRhCiAgICAgICByb3duYW1lcyhkZWxpdG9zX2ZyYW5qYV9tYXRyaXgpLAogICAgICAgY29sID0gY29sb3JlcywKICAgICAgIGx0eSA9IDEsCiAgICAgICBsd2QgPSA0LCAgIyBHcm9zb3IgZGUgbGFzIGzDrW5lYXMKICAgICAgIG5jb2wgPSAyLCAgIyBEaXZpZGlyIGxhIGxleWVuZGEgZW4gZG9zIGNvbHVtbmFzCiAgICAgICBjZXggPSAwLjksICAjIFRhbWHDsW8gZGVsIHRleHRvIG3DoXMgZ3JhbmRlCiAgICAgICB4cGQgPSBUUlVFKSAgIyBQZXJtaXRpciBxdWUgbGEgbGV5ZW5kYSBlc3TDqSBmdWVyYSBkZWwgw6FyZWEgZGUgZGlidWpvCgojIEd1YXJkYXIgeSBjZXJyYXIgZWwgYXJjaGl2byBQTkcKZGV2Lm9mZigpCgojIFZlcmlmaWNhciBxdWUgZWwgYXJjaGl2byBmdWUgY3JlYWRvCmJyb3dzZVVSTCgiZ3JhZmljb19mcmFuamFzX3Npbl9mZGEucG5nIikKYGBgCiFbQ3VydmFzIHBvciBGcmFuamEgU2luIEZEQSBuaSBzdWF2aXphZG9dKGdyYWZpY29fZnJhbmphc19zaW5fZmRhLnBuZykKCiMjIyBiKSBDT04gRkRBOiBDcmVhbW9zIGxhcyBjdXJ2YXMgcG9yIGZyYW5qYSBhIGxvIGxhcmdvIGRlbCAyMDIzIGNvbiBGREEuCgpgYGB7cn0KIyBQYXNvIDI6IFJlcHJlc2VudGFyIERhdG9zIGNvbW8gQ3VydmFzIEZ1bmNpb25hbGVzIGNvbiBTdWF2aXphZG8KCiMgQ3JlYXIgdW5hIGJhc2UgQi1zcGxpbmUgcGVuYWxpemFkYSBwYXJhIHN1YXZpemFyIGxhcyBmdW5jaW9uZXMKbmJhc2lzIDwtIDI1ICAjIEFqdXN0YSBlbCBuw7ptZXJvIGRlIGJhc2VzIHBhcmEgbWF5b3IgZmxleGliaWxpZGFkCmxhbWJkYSA8LSAxZS0yICAjIFBhcsOhbWV0cm8gZGUgc3Vhdml6YWRvOyBtZW5vciB2YWxvciA9IG3DoXMgc3VhdmUKCiMgQ3JlYXIgbGEgYmFzZSBCLXNwbGluZQpiYXNpcyA8LSBjcmVhdGUuYnNwbGluZS5iYXNpcyhyYW5nZXZhbCA9IHJhbmdlKGZlY2hhc19kYXRlKSwgbmJhc2lzID0gbmJhc2lzKQoKIyBEZWZpbmlyIGVsIG9iamV0byBkZSBwYXLDoW1ldHJvcyBmdW5jaW9uYWxlcyBjb24gc3Vhdml6YWRvCmZkX3BhciA8LSBmZFBhcihiYXNpcywgTGZkb2JqID0gaW50MkxmZCgyKSwgbGFtYmRhID0gbGFtYmRhKQoKIyBDb252ZXJ0aXIgbGFzIGZpbGFzIGRlIGxhIG1hdHJpeiBlbiBmdW5jaW9uZXMgY29udGludWFzIHN1YXZpemFkYXMKZmRfb2JqX3N1YXZpemFkbyA8LSBzbW9vdGguYmFzaXMoZmVjaGFzX2RhdGUsIHQoZGVsaXRvc19mcmFuamFfbWF0cml4KSwgZmRfcGFyKSRmZAoKIyBWaXN1YWxpemFyIGxhcyBmdW5jaW9uZXMgc3Vhdml6YWRhcyBwb3IgYmFycmlvIGFudGVzIGRlbCBjbHVzdGVyaW5nCmNvbG9yZXMgPC0gcmFpbmJvdyhucm93KGRlbGl0b3NfZnJhbmphX21hdHJpeCkpICAjIEdlbmVyYXIgY29sb3JlcyDDum5pY29zIHBhcmEgY2FkYSBiYXJyaW8KCiMgQ3JlYXIgZWwgZ3LDoWZpY28gY29uIGRpbWVuc2lvbmVzIGFtcGxpYWRhcwpwbmcoImdyYWZpY29fZnJhbmphc19jb25fZmRhLnBuZyIsIHdpZHRoID0gMjAwMCwgaGVpZ2h0ID0gMTIwMCwgcmVzID0gMTUwKSAgIyBSZXNvbHVjacOzbiBtw6FzIGFsdGEKCiMgQWp1c3RhciBtw6FyZ2VuZXM6IG3DoXMgZXNwYWNpbyBkZXJlY2hvIHBhcmEgbGEgbGV5ZW5kYQpwYXIobWFyID0gYygxMCwgNCwgNCwgMzUpICsgMC4xKSAgIyBBdW1lbnRhciBtYXJnZW4gZGVyZWNobyBwYXJhIHF1ZSBsYSBsZXllbmRhIG5vIHF1ZWRlIGNvcnRhZGEKCiMgR3JhZmljYXIgbGFzIGZ1bmNpb25lcyBzdWF2aXphZGFzCnBsb3QoZmRfb2JqX3N1YXZpemFkbywgbWFpbiA9ICJDdXJ2YXMgcG9yIEZyYW5qYSBzdWF2aXphZGFzIGNvbiBGREEiLAogICAgIHhsYWIgPSAiRmVjaGEiLCB5bGFiID0gIkZyZWN1ZW5jaWEiLAogICAgIGNvbCA9IGNvbG9yZXMsCiAgICAgbHdkID0gMiwgeGF4dCA9ICJuIikgICMgRGVzYWN0aXZhciBlamUgWCBwcmVkZXRlcm1pbmFkbwoKIyBDcmVhciBldGlxdWV0YXMgZGUgZmVjaGFzIGVuIGVsIGVqZSBYCmZlY2hhc19ldGlxdWV0YXMgPC0gc2VxKG1pbihmZWNoYXNfZGF0ZSksIG1heChmZWNoYXNfZGF0ZSksIGJ5ID0gIjIgd2Vla3MiKSAgIyBNb3N0cmFyIGNhZGEgZG9zIHNlbWFuYXMKYXhpcygxLAogICAgIGF0ID0gYXMubnVtZXJpYyhmZWNoYXNfZXRpcXVldGFzKSwgICMgVXNhciBmZWNoYXMgcmVhbGVzIGNvbW8gw61uZGljZXMKICAgICBsYWJlbHMgPSBmb3JtYXQoZmVjaGFzX2V0aXF1ZXRhcywgIiVkICViIiksICAjIETDrWEgeSBtZXMKICAgICBsYXMgPSAyLCAgIyBSb3RhciBsYXMgZXRpcXVldGFzCiAgICAgY2V4LmF4aXMgPSAwLjgpICAjIFRhbWHDsW8gZGVsIHRleHRvIGVuIGVsIGVqZSBYCgojIENyZWFyIHVuYSBsZXllbmRhIGFqdXN0YWRhIGVuIGVsIGxhZG8gZGVyZWNobyBkZWwgZ3LDoWZpY28KbGVnZW5kKCJ0b3BsZWZ0IiwKICAgICAgIGluc2V0ID0gYygxLjEsIDApLCAgIyBSZWR1Y2lyIGVsIGVzcGFjaW8gaGFjaWEgbGEgZGVyZWNoYQogICAgICAgbGVnZW5kID0gcm93bmFtZXMoZGVsaXRvc19mcmFuamFfbWF0cml4KSwKICAgICAgIGNvbCA9IGNvbG9yZXMsCiAgICAgICBsdHkgPSAxLAogICAgICAgbHdkID0gNCwgICMgR3Jvc29yIGRlIGxhcyBsw61uZWFzCiAgICAgICBuY29sID0gMiwgICMgRGl2aWRpciBsYSBsZXllbmRhIGVuIGRvcyBjb2x1bW5hcwogICAgICAgY2V4ID0gMC45LCAgIyBUYW1hw7FvIGRlbCB0ZXh0byBhanVzdGFkbwogICAgICAgeHBkID0gVFJVRSkgICMgUGVybWl0aXIgcXVlIGxhIGxleWVuZGEgZXN0w6kgZnVlcmEgZGVsIMOhcmVhIGRlIGRpYnVqbwoKCiMgR3VhcmRhciB5IGNlcnJhciBlbCBhcmNoaXZvIFBORwpkZXYub2ZmKCkKCiMgVmVyaWZpY2FyIHF1ZSBlbCBhcmNoaXZvIGZ1ZSBjcmVhZG8KYnJvd3NlVVJMKCJncmFmaWNvX2ZyYW5qYXNfY29uX2ZkYS5wbmciKQoKYGBgCgohW0N1cnZhcyBwb3IgRnJhbmphIHN1YXZpemFkYXMgY29uIEZEQV0oZ3JhZmljb19mcmFuamFzX2Nvbl9mZGEucG5nKQoKIyMgQikgRnJlbmN1ZW5jaWEgZGUgZGVsaXRvcyBhZ3J1cGFkb3MgcG9yIGJhcnJpbyAoNDgpIHkgZmVjaGEKCkFybWFtb3MgbGEgYWdydXBhY2nDs24gcG9yIGJhcnJpbyB5IGZlY2hhIHkgY3JlYW1vcyBsYSBtYXRyaXogZnVuY2luYWwgY29uIGJhcnJpb3MgY29tbyBmaWxhcyB5IGZlY2hhcyBjb21vIGNvbHVtbmFzCgpgYGB7cn0KIyBDYXJnYXIgbGlicmVyw61hcyBuZWNlc2FyaWFzCmlmICghcmVxdWlyZSgiUkNvbG9yQnJld2VyIikpIGluc3RhbGwucGFja2FnZXMoIlJDb2xvckJyZXdlciIpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGZkYS51c2MpCmxpYnJhcnkoZmRhKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeSh0aWJibGUpICAjIFBhcmEgdXNhciBjb2x1bW5fdG9fcm93bmFtZXMKCiMgUGFzbyAxOiBBZ3J1cGFyIGRhdG9zIHkgcHJlcGFyYXIgbGEgbWF0cml6IGZ1bmNpb25hbAojIEFncnVwYXIgcG9yIGJhcnJpbyB5IGZlY2hhLCBzdW1hbmRvIGxhIGZyZWN1ZW5jaWEgZGUgZGVsaXRvcwpkZWxpdG9zX2FncnVwYWRvc19iYXJyaW8gPC0gZGVsaXRvcyAlPiUKICBncm91cF9ieShiYXJyaW8sIGZlY2hhKSAlPiUKICBzdW1tYXJpc2UoZnJlY3VlbmNpYV9kaWFyaWEgPSBzdW0oY2FudGlkYWQsIG5hLnJtID0gVFJVRSksIC5ncm91cHMgPSAiZHJvcCIpCgojIENyZWFyIG1hdHJpeiBmdW5jaW9uYWw6IGJhcnJpb3MgY29tbyBmaWxhcywgZmVjaGFzIGNvbW8gY29sdW1uYXMKZGVsaXRvc19tYXRyaXggPC0gZGVsaXRvc19hZ3J1cGFkb3NfYmFycmlvICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBmZWNoYSwgdmFsdWVzX2Zyb20gPSBmcmVjdWVuY2lhX2RpYXJpYSwgdmFsdWVzX2ZpbGwgPSAwKSAlPiUKICBjb2x1bW5fdG9fcm93bmFtZXModmFyID0gImJhcnJpbyIpICU+JQogIGFzLm1hdHJpeCgpCgojIEVsaW1pbmFyIGZpbGFzIGNvbiB2YWxvcmVzIGZhbHRhbnRlcyB5IHZlcmlmaWNhciBsYSB2YXJpYWJpbGlkYWQKZGVsaXRvc19tYXRyaXggPC0gZGVsaXRvc19tYXRyaXhbY29tcGxldGUuY2FzZXMoZGVsaXRvc19tYXRyaXgpLCBdCnZhcmlhYmlsaWRhZCA8LSBhcHBseShkZWxpdG9zX21hdHJpeCwgMSwgdmFyLCBuYS5ybSA9IFRSVUUpCmRlbGl0b3NfbWF0cml4IDwtIGRlbGl0b3NfbWF0cml4W3ZhcmlhYmlsaWRhZCA+IDAuMDEsIF0KCgpgYGAKCiMjIyBhKSBTSU4gRkRBOiBDcmVhbW9zIGxhcyBjdXJ2YXMgcG9yIGJhcnJpbyBhIGxvIGxhcmdvIGRlbCAyMDIzIHNpbiBGREEuCgpgYGB7cn0KCiMgQ3JlYXIgdW4gdmVjdG9yIGRlIGZlY2hhcyByZWFsZXMKZmVjaGFzX2RhdGUgPC0gc2VxLkRhdGUoZnJvbSA9IGFzLkRhdGUoIjIwMjMtMDEtMDEiKSwgYnkgPSAiZGF5IiwgbGVuZ3RoLm91dCA9IG5jb2woZGVsaXRvc19tYXRyaXgpKQoKIyBHcmFmaWNhciBjdXJ2YXMgb3JpZ2luYWxlcyBwb3IgYmFycmlvIHNpbiBhcGxpY2FyIEZEQQojIENyZWFyIGVsIGdyw6FmaWNvIGNvbiBkaW1lbnNpb25lcyBhbXBsaWFkYXMKcG5nKCJncmFmaWNvX2JhcnJpb3Nfc2luX2ZkYS5wbmciLCB3aWR0aCA9IDIwMDAsIGhlaWdodCA9IDEyMDAsIHJlcyA9IDE1MCkgICMgUmVzb2x1Y2nDs24gbcOhcyBhbHRhCgojIEFqdXN0YXIgbcOhcmdlbmVzOiBtw6FzIGVzcGFjaW8gaXpxdWllcmRvIHBhcmEgbGEgbGV5ZW5kYQpwYXIobWFyID0gYygxMCwgMzUsIDQsIDQpICsgMC4xKSAgIyBBdW1lbnRhciBtYXJnZW4gaXpxdWllcmRvCgojIENyZWFyIGNvbG9yZXMgw7puaWNvcyBwYXJhIGNhZGEgYmFycmlvCmNvbG9yZXMgPC0gcmFpbmJvdyhucm93KGRlbGl0b3NfbWF0cml4KSkKCiMgR3JhZmljYXIgbGFzIGN1cnZhcyBvcmlnaW5hbGVzCm1hdHBsb3QoZmVjaGFzX2RhdGUsIHQoZGVsaXRvc19tYXRyaXgpLCB0eXBlID0gImwiLCBsdHkgPSAxLCBjb2wgPSBjb2xvcmVzLCBsd2QgPSAyLAogICAgICAgIHhsYWIgPSAiRmVjaGEiLCB5bGFiID0gIkZyZWN1ZW5jaWEiLCB4YXh0ID0gIm4iLAogICAgICAgIG1haW4gPSAiQ3VydmFzIHBvciBCYXJyaW8gKFNpbiBzdWF2aXphZG8pIikKCiMgQ3JlYXIgZXRpcXVldGFzIGRlIGZlY2hhcyBlbiBlbCBlamUgWApmZWNoYXNfZXRpcXVldGFzIDwtIHNlcShtaW4oZmVjaGFzX2RhdGUpLCBtYXgoZmVjaGFzX2RhdGUpLCBieSA9ICIyIHdlZWtzIikgICMgTW9zdHJhciBjYWRhIGRvcyBzZW1hbmFzCmF4aXMoMSwKICAgICBhdCA9IGFzLm51bWVyaWMoZmVjaGFzX2V0aXF1ZXRhcyksICAjIFVzYXIgZmVjaGFzIHJlYWxlcyBjb21vIMOtbmRpY2VzCiAgICAgbGFiZWxzID0gZm9ybWF0KGZlY2hhc19ldGlxdWV0YXMsICIlZCAlYiIpLCAgIyBEw61hIHkgbWVzCiAgICAgbGFzID0gMiwgICMgUm90YXIgbGFzIGV0aXF1ZXRhcwogICAgIGNleC5heGlzID0gMC44KSAgIyBUYW1hw7FvIGRlbCB0ZXh0byBlbiBlbCBlamUgWHBlCgojIENyZWFyIHVuYSBsZXllbmRhIG3DoXMgYW1wbGlhIGVuIGVsIG1hcmdlbiBpenF1aWVyZG8KbGVnZW5kKCJ0b3ByaWdodCIsCiAgICAgICBpbnNldCA9IGMoMS4yLCAwKSwgICAjIE3DoXMgZXNwYWNpbyBoYWNpYSBsYSBpenF1aWVyZGEKICAgICAgIHJvd25hbWVzKGRlbGl0b3NfbWF0cml4KSwKICAgICAgIGNvbCA9IGNvbG9yZXMsCiAgICAgICBsdHkgPSAxLAogICAgICAgbHdkID0gNCwgICMgR3Jvc29yIGRlIGxhcyBsw61uZWFzCiAgICAgICBuY29sID0gMiwgICMgRGl2aWRpciBsYSBsZXllbmRhIGVuIGRvcyBjb2x1bW5hcwogICAgICAgY2V4ID0gMC45LCAgIyBUYW1hw7FvIGRlbCB0ZXh0byBtw6FzIGdyYW5kZQogICAgICAgeHBkID0gVFJVRSkgICMgUGVybWl0aXIgcXVlIGxhIGxleWVuZGEgZXN0w6kgZnVlcmEgZGVsIMOhcmVhIGRlIGRpYnVqbwoKIyBHdWFyZGFyIHkgY2VycmFyIGVsIGFyY2hpdm8gUE5HCmRldi5vZmYoKQoKIyBWZXJpZmljYXIgcXVlIGVsIGFyY2hpdm8gZnVlIGNyZWFkbwpicm93c2VVUkwoImdyYWZpY29fYmFycmlvc19zaW5fZmRhLnBuZyIpCgpgYGAKCiFbQ3VydmFzIHBvciBCYXJyaW8gKFNpbiBGREEgbmkgc3Vhdml6YWRvKV0oZ3JhZmljb19iYXJyaW9zX3Npbl9mZGEucG5nKQoKIyMjIEdyYWZpY2Ftb3MgZWwgbcOpdG9kbyBkZSBjb2RvIHkgc2lsaG91ZXR0ZSBwYXJhIGRlZmluaXIgZWwgbsO6bWVybyBkZSBjbHVzdGVycwoKYGBge3J9CmRpbShkZWxpdG9zX21hdHJpeCkgICMgRGViZXLDrWFzIHZlciAobl9iYXJyaW8sIG5fZmVjaGFzKQoKZGVsaXRvc19tYXRyaXhfbm9ybWFsaXplZCA8LSBzY2FsZShkZWxpdG9zX21hdHJpeCkKCmxpYnJhcnkoZmFjdG9leHRyYSkKCiMgTcOpdG9kbyBkZWwgY29kbyBwYXJhIGRldGVybWluYXIgZWwgbsO6bWVybyDDs3B0aW1vIGRlIGNsdXN0ZXJzCmZ2aXpfbmJjbHVzdChkZWxpdG9zX21hdHJpeF9ub3JtYWxpemVkLCBrbWVhbnMsIG1ldGhvZCA9ICJ3c3MiKSArCiAgbGFicyh0aXRsZSA9ICJNw6l0b2RvIGRlbCBDb2RvIikKCmZ2aXpfbmJjbHVzdChkZWxpdG9zX21hdHJpeF9ub3JtYWxpemVkLCBrbWVhbnMsIG1ldGhvZCA9ICJzaWxob3VldHRlIikgKwogIGxhYnModGl0bGUgPSAiw41uZGljZSBkZSBTaWxob3VldHRlIikKCmBgYAoKYGBge3J9CnNldC5zZWVkKDEyMykgICMgUGFyYSByZXByb2R1Y2liaWxpZGFkCm51bV9jbHVzdGVycyA8LSAyICAjIENhbWJpYSBzZWfDum4gbG9zIHJlc3VsdGFkb3MgZGVsIG3DqXRvZG8gZGVsIGNvZG8gbyBTaWxob3VldHRlCgprbWVhbnNfcmVzdWx0IDwtIGttZWFucyhkZWxpdG9zX21hdHJpeF9ub3JtYWxpemVkLCBjZW50ZXJzID0gbnVtX2NsdXN0ZXJzLCBuc3RhcnQgPSAyMCkKCmNsdXN0ZXJzIDwtIGRhdGEuZnJhbWUoCiAgYmFycmlvID0gcm93bmFtZXMoZGVsaXRvc19tYXRyaXgpLAogIGNsdXN0ZXIgPSBrbWVhbnNfcmVzdWx0JGNsdXN0ZXIKKQoKIyBPcmRlbmFyIGVsIGRhdGFmcmFtZSBwb3IgY2x1c3RlcgpjbHVzdGVyc19vcmRlbmFkb3MgPC0gY2x1c3RlcnMgJT4lCiAgYXJyYW5nZShjbHVzdGVyKQoKCiMgVmVyIGxvcyBwcmltZXJvcyByZXN1bHRhZG9zCnByaW50KGNsdXN0ZXJzX29yZGVuYWRvcykKCgpgYGAKClVzYW1vcyBQQ0EgcGFyYSBncmFmaWNhciBsb3MgY2x1c3RlcnMKCmBgYHtyfQoKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyKQoKIyBDYWxjdWxhciBsYSBjYW50aWRhZCBkZSBiYXJyaW9zIHBvciBjbHVzdGVyCmNsdXN0ZXJfc2l6ZXMgPC0gY2x1c3RlcnMgJT4lCiAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lCiAgc3VtbWFyaXNlKG5fYmFycmlvcyA9IG4oKSkgJT4lCiAgbXV0YXRlKGNsdXN0ZXJfbGFiZWwgPSBwYXN0ZTAoIkNsdXN0ZXIgIiwgY2x1c3RlciwgIiAobj0iLCBuX2JhcnJpb3MsICIpIikpCgojIENhbGN1bGFyIFBDQSBlbiBsYSBtYXRyaXogbm9ybWFsaXphZGEKcGNhX3Jlc3VsdCA8LSBwcmNvbXAoZGVsaXRvc19tYXRyaXhfbm9ybWFsaXplZCwgY2VudGVyID0gVFJVRSwgc2NhbGUuID0gVFJVRSkKcGNhX2RhdGEgPC0gYXMuZGF0YS5mcmFtZShwY2FfcmVzdWx0JHhbLCAxOjJdKSAgIyBUb21hciBsYXMgZG9zIHByaW1lcmFzIGNvbXBvbmVudGVzCnBjYV9kYXRhJGNsdXN0ZXIgPC0gYXMuZmFjdG9yKGNsdXN0ZXJzJGNsdXN0ZXIpICAjIENvbnZlcnRpciBhIGZhY3RvcgoKY2x1c3Rlcl9zaXplcyRjbHVzdGVyIDwtIGFzLmZhY3RvcihjbHVzdGVyX3NpemVzJGNsdXN0ZXIpICAjIENvbnZlcnRpciBhIGZhY3RvcgoKIyBDb21iaW5hciBjb24gbGFzIGV0aXF1ZXRhcyBwZXJzb25hbGl6YWRhcwpwY2FfZGF0YSA8LSBwY2FfZGF0YSAlPiUKICBsZWZ0X2pvaW4oY2x1c3Rlcl9zaXplcywgYnkgPSAiY2x1c3RlciIpCgojIEdyYWZpY2FyIFBDQSBjb24gbGV5ZW5kYSBwZXJzb25hbGl6YWRhCmdncGxvdChwY2FfZGF0YSwgYWVzKHggPSBQQzEsIHkgPSBQQzIsIGNvbG9yID0gY2x1c3Rlcl9sYWJlbCkpICsKICBnZW9tX3BvaW50KHNpemUgPSAzLCBhbHBoYSA9IDAuNykgKyAgIyBQdW50b3MgcGFyYSBsb3MgYmFycmlvcwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicygKICAgIHRpdGxlID0gIkNsdXN0ZXJzIFZpc3VhbGl6YWRvcyBlbiBlbCBFc3BhY2lvIFBDQSIsCiAgICB4ID0gIlBDMSIsCiAgICB5ID0gIlBDMiIsCiAgICBjb2xvciA9ICJDbHVzdGVyIgogICkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKCmBgYAoKYGBge3J9CmRlbGl0b3NfZGYgPC0gYXMuZGF0YS5mcmFtZShkZWxpdG9zX21hdHJpeCkgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJiYXJyaW8iKSAgIyBDb252ZXJ0aXIgcm93bmFtZXMgZW4gY29sdW1uYSBsbGFtYWRhICdiYXJyaW8nCnJlc3VtZW5fY2x1c3RlcnMgPC0gY2x1c3RlcnMgJT4lCiAgbGVmdF9qb2luKGRlbGl0b3NfZGYsIGJ5ID0gImJhcnJpbyIpICU+JSAgIyAnYmFycmlvJyB5YSBlcyB1bmEgY29sdW1uYSBleHBsw61jaXRhCiAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lCiAgc3VtbWFyaXNlX2FsbChtZWFuLCBuYS5ybSA9IFRSVUUpCgpwcmludChyZXN1bWVuX2NsdXN0ZXJzKQoKYGBgCgojIyMgQXBsaWNhbW9zIHNpbGhvdWV0dGUgcGFyYSB2YWxpZGFyIGxvcyBjbHVzdGVycwoKYGBge3J9CmxpYnJhcnkoY2x1c3RlcikKCiMgQ2FsY3VsYXIgw61uZGljZSBkZSBTaWxob3VldHRlCnNpbGhvdWV0dGVfcmVzdWx0IDwtIHNpbGhvdWV0dGUoa21lYW5zX3Jlc3VsdCRjbHVzdGVyLCBkaXN0KGRlbGl0b3NfbWF0cml4X25vcm1hbGl6ZWQpKQoKIyBSZXN1bWVuIGRlbCDDrW5kaWNlIGRlIFNpbGhvdWV0dGUKc3VtbWFyeShzaWxob3VldHRlX3Jlc3VsdCkKCiMgSW5zcGVjY2lvbmFyIGxvcyB2YWxvcmVzIGRlbCDDrW5kaWNlIGRlIFNpbGhvdWV0dGUKaGVhZChzaWxob3VldHRlX3Jlc3VsdFssIDE6M10pICAjIENsdXN0ZXIsIHZlY2lubyBtw6FzIGNlcmNhbm8sIHZhbG9yIGRlIFNpbGhvdWV0dGUKCiMgUHJvbWVkaW8gZGVsIMOtbmRpY2UKbWVhbihzaWxob3VldHRlX3Jlc3VsdFssIDNdKQoKIyBWaXN1YWxpemFyIMOtbmRpY2UgZGUgU2lsaG91ZXR0ZQpmdml6X3NpbGhvdWV0dGUoc2lsaG91ZXR0ZV9yZXN1bHQpICsKICBsYWJzKHRpdGxlID0gIsONbmRpY2UgZGUgU2lsaG91ZXR0ZSBwb3IgQ2x1c3RlciIsCiAgICAgICB4ID0gIsONbmRpY2UgZGUgU2lsaG91ZXR0ZSIsCiAgICAgICB5ID0gIkJhcnJpb3MiKQoKYGBgCgoKIyMgYikgQ09OIEZEQTogQ3JlYW1vcyBsYXMgY3VydmFzIHBvciBiYXJyaW8gYSBsbyBsYXJnbyBkZWwgMjAyMyBjb24gRkRBLgoKYGBge3J9CgojIFBhc28gMjogUmVwcmVzZW50YXIgRGF0b3MgY29tbyBDdXJ2YXMgRnVuY2lvbmFsZXMgY29uIFN1YXZpemFkbwoKIyBDcmVhciB1bmEgYmFzZSBCLXNwbGluZSBwZW5hbGl6YWRhIHBhcmEgc3Vhdml6YXIgbGFzIGZ1bmNpb25lcwpuYmFzaXMgPC0gMjUgICMgQWp1c3RhIGVsIG7Dum1lcm8gZGUgYmFzZXMgcGFyYSBtYXlvciBmbGV4aWJpbGlkYWQKbGFtYmRhIDwtIDFlLTIgICMgUGFyw6FtZXRybyBkZSBzdWF2aXphZG87IG1lbm9yIHZhbG9yID0gbcOhcyBzdWF2ZQoKIyBDcmVhciBsYSBiYXNlIEItc3BsaW5lCmJhc2lzIDwtIGNyZWF0ZS5ic3BsaW5lLmJhc2lzKHJhbmdldmFsID0gcmFuZ2UoZmVjaGFzX2RhdGUpLCBuYmFzaXMgPSBuYmFzaXMpCgojIERlZmluaXIgZWwgb2JqZXRvIGRlIHBhcsOhbWV0cm9zIGZ1bmNpb25hbGVzIGNvbiBzdWF2aXphZG8KZmRfcGFyIDwtIGZkUGFyKGJhc2lzLCBMZmRvYmogPSBpbnQyTGZkKDIpLCBsYW1iZGEgPSBsYW1iZGEpCgojIENvbnZlcnRpciBsYXMgZmlsYXMgZGUgbGEgbWF0cml6IGVuIGZ1bmNpb25lcyBjb250aW51YXMgc3Vhdml6YWRhcwpmZF9vYmpfc3Vhdml6YWRvIDwtIHNtb290aC5iYXNpcyhmZWNoYXNfZGF0ZSwgdChkZWxpdG9zX21hdHJpeCksIGZkX3BhcikkZmQKCiMgVmlzdWFsaXphciBsYXMgZnVuY2lvbmVzIHN1YXZpemFkYXMgcG9yIGJhcnJpbyBhbnRlcyBkZWwgY2x1c3RlcmluZwpjb2xvcmVzIDwtIHJhaW5ib3cobnJvdyhkZWxpdG9zX21hdHJpeCkpICAjIEdlbmVyYXIgY29sb3JlcyDDum5pY29zIHBhcmEgY2FkYSBiYXJyaW8KCiMgQ3JlYXIgZWwgZ3LDoWZpY28gY29uIGRpbWVuc2lvbmVzIGFtcGxpYWRhcwpwbmcoImdyYWZpY29fYmFycmlvc19jb25fZmRhLnBuZyIsIHdpZHRoID0gMjAwMCwgaGVpZ2h0ID0gMTIwMCwgcmVzID0gMTUwKSAgIyBSZXNvbHVjacOzbiBtw6FzIGFsdGEKCiMgQWp1c3RhciBtw6FyZ2VuZXM6IG3DoXMgZXNwYWNpbyBkZXJlY2hvIHBhcmEgbGEgbGV5ZW5kYQpwYXIobWFyID0gYygxMCwgNCwgNCwgMzUpICsgMC4xKSAgIyBBdW1lbnRhciBtYXJnZW4gZGVyZWNobyBwYXJhIHF1ZSBsYSBsZXllbmRhIG5vIHF1ZWRlIGNvcnRhZGEKCiMgR3JhZmljYXIgbGFzIGZ1bmNpb25lcyBzdWF2aXphZGFzCnBsb3QoZmRfb2JqX3N1YXZpemFkbywgbWFpbiA9ICJDdXJ2YXMgcG9yIEJhcnJpbyBzdWF2aXphZGFzIGNvbiBGREEiLAogICAgIHhsYWIgPSAiRmVjaGEiLCB5bGFiID0gIkZyZWN1ZW5jaWEiLAogICAgIGNvbCA9IGNvbG9yZXMsCiAgICAgbHdkID0gMiwgeGF4dCA9ICJuIikgICMgRGVzYWN0aXZhciBlamUgWCBwcmVkZXRlcm1pbmFkbwoKIyBDcmVhciBldGlxdWV0YXMgZGUgZmVjaGFzIGVuIGVsIGVqZSBYCmZlY2hhc19ldGlxdWV0YXMgPC0gc2VxKG1pbihmZWNoYXNfZGF0ZSksIG1heChmZWNoYXNfZGF0ZSksIGJ5ID0gIjIgd2Vla3MiKSAgIyBNb3N0cmFyIGNhZGEgZG9zIHNlbWFuYXMKYXhpcygxLAogICAgIGF0ID0gYXMubnVtZXJpYyhmZWNoYXNfZXRpcXVldGFzKSwgICMgVXNhciBmZWNoYXMgcmVhbGVzIGNvbW8gw61uZGljZXMKICAgICBsYWJlbHMgPSBmb3JtYXQoZmVjaGFzX2V0aXF1ZXRhcywgIiVkICViIiksICAjIETDrWEgeSBtZXMKICAgICBsYXMgPSAyLCAgIyBSb3RhciBsYXMgZXRpcXVldGFzCiAgICAgY2V4LmF4aXMgPSAwLjgpICAjIFRhbWHDsW8gZGVsIHRleHRvIGVuIGVsIGVqZSBYCgojIENyZWFyIHVuYSBsZXllbmRhIGFqdXN0YWRhIGVuIGVsIGxhZG8gZGVyZWNobyBkZWwgZ3LDoWZpY28KbGVnZW5kKCJ0b3BsZWZ0IiwKICAgICAgIGluc2V0ID0gYygxLjEsIDApLCAgIyBSZWR1Y2lyIGVsIGVzcGFjaW8gaGFjaWEgbGEgZGVyZWNoYQogICAgICAgbGVnZW5kID0gcm93bmFtZXMoZGVsaXRvc19tYXRyaXgpLAogICAgICAgY29sID0gY29sb3JlcywKICAgICAgIGx0eSA9IDEsCiAgICAgICBsd2QgPSA0LCAgIyBHcm9zb3IgZGUgbGFzIGzDrW5lYXMKICAgICAgIG5jb2wgPSAyLCAgIyBEaXZpZGlyIGxhIGxleWVuZGEgZW4gZG9zIGNvbHVtbmFzCiAgICAgICBjZXggPSAwLjksICAjIFRhbWHDsW8gZGVsIHRleHRvIGFqdXN0YWRvCiAgICAgICB4cGQgPSBUUlVFKSAgIyBQZXJtaXRpciBxdWUgbGEgbGV5ZW5kYSBlc3TDqSBmdWVyYSBkZWwgw6FyZWEgZGUgZGlidWpvCgoKIyBHdWFyZGFyIHkgY2VycmFyIGVsIGFyY2hpdm8gUE5HCmRldi5vZmYoKQoKIyBWZXJpZmljYXIgcXVlIGVsIGFyY2hpdm8gZnVlIGNyZWFkbwpicm93c2VVUkwoImdyYWZpY29fYmFycmlvc19jb25fZmRhLnBuZyIpCgojIFBhc28gMzogQ2x1c3RlcmluZyBGdW5jaW9uYWwgY29uIEstTWVhbnMKIyBEZXRlcm1pbmFyIGVsIG7Dum1lcm8gw7NwdGltbyBkZSBjbHVzdGVycyBjb24gZWwgbcOpdG9kbyBkZWwgY29kbwpzZXQuc2VlZCgxMjMpCndzcyA8LSBzYXBwbHkoMjoxMCwgZnVuY3Rpb24oaykgewogIGttZWFucyhmZF9vYmpfc3Vhdml6YWRvJGNvZWZzLCBjZW50ZXJzID0gaywgbnN0YXJ0ID0gMjApJHRvdC53aXRoaW5zcwp9KQoKYGBgCgohW0N1cnZhcyBwb3IgQmFycmlvIHN1YXZpemFkYSBjb24gRkRBKV0oZ3JhZmljb19iYXJyaW9zX2Nvbl9mZGEucG5nKQoKIyMjIEdyYWZpY2Ftb3MgZWwgbcOpdG9kbyBkZSBjb2RvIHkgc2lsaG91ZXR0ZSBwYXJhIGRlZmluaXIgZWwgbsO6bWVybyBkZSBjbHVzdGVycwoKYGBge3J9CiMgR3JhZmljYXIgZWwgbcOpdG9kbyBkZWwgY29kbwpwbG90KDI6MTAsIHdzcywgdHlwZSA9ICJiIiwgcGNoID0gMTksIGZyYW1lID0gRkFMU0UsCiAgICAgeGxhYiA9ICJOw7ptZXJvIGRlIENsdXN0ZXJzIiwgeWxhYiA9ICJTdW1hIGRlIEVycm9yZXMgYWwgQ3VhZHJhZG8gKFdTUykiLAogICAgIG1haW4gPSAiTcOpdG9kbyBkZWwgQ29kbyBwYXJhIENsdXN0ZXJpbmcgRnVuY2lvbmFsIikKCiMgRWxlZ2lyIHVuIG7Dum1lcm8gZGUgY2x1c3RlcnMgKHBvciBlamVtcGxvLCAzKQpudW1fY2x1c3RlcnMgPC0gMgprbWVhbnNfcmVzdWx0IDwtIGttZWFucyhmZF9vYmpfc3Vhdml6YWRvJGNvZWZzLCBjZW50ZXJzID0gbnVtX2NsdXN0ZXJzLCBuc3RhcnQgPSAyMCkKCmZ2aXpfbmJjbHVzdChmZF9vYmpfc3Vhdml6YWRvJGNvZWZzLCBrbWVhbnMsIG1ldGhvZCA9ICJzaWxob3VldHRlIikgKwogIGxhYnModGl0bGUgPSAiw41uZGljZSBkZSBTaWxob3VldHRlIikKYGBgCgojIyMgRGVmaW5pbW9zIGxvcyBjbHVzdGVycyBjb3JyZXNwb25kaWVudGVzCgpgYGB7cn0KCmZkX3RyYW5zcG9zZWQgPC0gdChmZF9vYmpfc3Vhdml6YWRvJGNvZWZzKQoKIyBBcGxpY2FyIGttZWFucyBmdW5jaW9uYWwgY29uIG1hbmVqbyBkZSBjbHVzdGVycyBwZXF1ZcOxb3MKbnVtX2NsdXN0ZXJzIDwtIDIgICMgQ2FtYmlhciBzZWfDum4gbmVjZXNpZGFkCgojIEFwbGljYXIgY2x1c3RlcmluZyBmdW5jaW9uYWwKa21lYW5zX3Jlc3VsdCA8LSBrbWVhbnModChmZF9vYmpfc3Vhdml6YWRvJGNvZWZzKSwgY2VudGVycyA9IG51bV9jbHVzdGVycywgbnN0YXJ0ID0gMjApCgpgYGAKCiMjIyBHcmFmaWNhbW9zIGxvcyBjbHVzdGVycyAKCgpgYGB7cn0KCiMgQXNpZ25hciBjbHVzdGVycyBhIGxvcyBiYXJyaW9zCiMgQ3JlYXIgZWwgZGF0YWZyYW1lIGNvbiBsb3MgYmFycmlvcyB5IGxvcyBjbHVzdGVycyBhc2lnbmFkb3MKY2x1c3RlcnMgPC0gZGF0YS5mcmFtZSgKICBiYXJyaW8gPSBjb2xuYW1lcyhmZF9vYmpfc3Vhdml6YWRvJGNvZWZzKSwgICMgTm9tYnJlcyBkZSBsb3MgYmFycmlvcwogIGNsdXN0ZXIgPSBrbWVhbnNfcmVzdWx0JGNsdXN0ZXIgICAgICAgICAgICAgICAjIENsdXN0ZXJzIGFzaWduYWRvcwopCgojIEV2YWx1YXIgbGFzIGN1cnZhcyBmdW5jaW9uYWxlcwptYXRyaXpfZXZhbHVhZGEgPC0gdChldmFsLmZkKGZlY2hhc19kYXRlLCBmZF9vYmpfc3Vhdml6YWRvKSkgICMgRXZhbHVhciBlbiBmZWNoYXNfZGF0ZQoKIyBBc2lnbmFyIGxhcyBmZWNoYXMgY29tbyBub21icmVzIGRlIGxhcyBjb2x1bW5hcwpjb2xuYW1lcyhtYXRyaXpfZXZhbHVhZGEpIDwtIGFzLmNoYXJhY3RlcihmZWNoYXNfZGF0ZSkgICMgQ29udmVydGlyIGZlY2hhcyBhIHRleHRvCgoKIyBBY3R1YWxpemFyIGV0aXF1ZXRhcyBkZSBsb3MgY2x1c3RlcnMgY29uIGxvcyB0YW1hw7FvcwpkZl9jdXJ2ZXMgPC0gYXMuZGF0YS5mcmFtZShtYXRyaXpfZXZhbHVhZGEpICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigiYmFycmlvIikgJT4lCiAgcGl2b3RfbG9uZ2VyKC1iYXJyaW8sIG5hbWVzX3RvID0gImZlY2hhIiwgdmFsdWVzX3RvID0gImZyZWN1ZW5jaWEiKSAlPiUKICBtdXRhdGUoCiAgICBmZWNoYSA9IGFzLkRhdGUoZmVjaGEpLCAgIyBDb252ZXJ0aXIgdGV4dG8gYSBvYmpldG8gRGF0ZQogICAgY2x1c3RlciA9IGZhY3RvcihjbHVzdGVycyRjbHVzdGVyW21hdGNoKGJhcnJpbywgY2x1c3RlcnMkYmFycmlvKV0pCiAgKQoKCiMgQ2FsY3VsYXIgY2FudGlkYWQgZGUgYmFycmlvcyDDum5pY29zIHBvciBjbHVzdGVyCmNsdXN0ZXJfc2l6ZXMgPC0gZGZfY3VydmVzICU+JQogIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQogIHN1bW1hcmlzZShuX2JhcnJpb3MgPSBuX2Rpc3RpbmN0KGJhcnJpbykpICAjIENvbnRhciBiYXJyaW9zIMO6bmljb3MKCmRmX2N1cnZlcyA8LSBkZl9jdXJ2ZXMgJT4lCiAgbGVmdF9qb2luKGNsdXN0ZXJfc2l6ZXMsIGJ5ID0gImNsdXN0ZXIiKSAlPiUgICMgQWdyZWdhciB0YW1hw7FvcyBhbCBkYXRhZnJhbWUKICBtdXRhdGUoY2x1c3Rlcl9sYWJlbCA9IHBhc3RlMCgiQ2x1c3RlciAiLCBjbHVzdGVyLCAiIChuPSIsIG5fYmFycmlvcywgIikiKSkgICMgRXRpcXVldGFzIHBlcnNvbmFsaXphZGFzCgoKZ2dwbG90KGRmX2N1cnZlcywgYWVzKHggPSBmZWNoYSwgeSA9IGZyZWN1ZW5jaWEsIGdyb3VwID0gYmFycmlvLCBjb2xvciA9IGNsdXN0ZXJfbGFiZWwpKSArCiAgZ2VvbV9saW5lKGFscGhhID0gMC43KSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKAogICAgdGl0bGUgPSAiQ2x1c3RlcmluZyBGdW5jaW9uYWwgcG9yIEJhcnJpbyIsCiAgICB4ID0gIkZlY2hhIiwKICAgIHkgPSAiRnJlY3VlbmNpYSIsCiAgICBjb2xvciA9ICJDbHVzdGVyIgogICkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKCiMgQ3JlYXIgbGEgdGFibGEgY29uIGxvcyBiYXJyaW9zIHBvciBjbHVzdGVyCnRhYmxhX2JhcnJpb3MgPC0gZGZfY3VydmVzICU+JQogIGRwbHlyOjpzZWxlY3QoYmFycmlvLCBjbHVzdGVyX2xhYmVsKSAlPiUgICMgU2VsZWNjaW9uYXIgY29sdW1uYXMgbmVjZXNhcmlhcwogIGRpc3RpbmN0KCkgJT4lICAgICAgICAgICAgICAgICAgICAgIyBFbGltaW5hciBkdXBsaWNhZG9zCiAgYXJyYW5nZShjbHVzdGVyX2xhYmVsLCBiYXJyaW8pICAgICAjIE9yZGVuYXIgcG9yIGNsdXN0ZXIgeSBiYXJyaW8KCnByaW50KHRhYmxhX2JhcnJpb3MpCgpgYGAKCiMjIyBHcmFmaWNhbW9zIGxhcyBjdXJ2YXMgcHJvbWVkaW9zIHBvciBjbHVzdGVyIAoKYGBge3J9CiMgQ2FsY3VsYXIgbGEgZnJlY3VlbmNpYSBwcm9tZWRpbyBwb3IgY2x1c3RlciB5IGZlY2hhCmRmX2F2ZyA8LSBkZl9jdXJ2ZXMgJT4lCiAgZ3JvdXBfYnkoY2x1c3Rlcl9sYWJlbCwgZmVjaGEpICU+JQogIHN1bW1hcmlzZShmcmVjdWVuY2lhX3Byb21lZGlvID0gbWVhbihmcmVjdWVuY2lhKSwgLmdyb3VwcyA9ICJkcm9wIikKCmdncGxvdCgpICsKICAjIEN1cnZhcyBpbmRpdmlkdWFsZXMKICBnZW9tX2xpbmUoZGF0YSA9IGRmX2N1cnZlcywgYWVzKHggPSBmZWNoYSwgeSA9IGZyZWN1ZW5jaWEsIGdyb3VwID0gYmFycmlvLCBjb2xvciA9IGNsdXN0ZXJfbGFiZWwpLCBhbHBoYSA9IDAuNCkgKwogICMgQ3VydmFzIHByb21lZGlvIHBvciBjbHVzdGVyCiAgZ2VvbV9saW5lKGRhdGEgPSBkZl9hdmcsIGFlcyh4ID0gZmVjaGEsIHkgPSBmcmVjdWVuY2lhX3Byb21lZGlvLCBncm91cCA9IGNsdXN0ZXJfbGFiZWwsIGNvbG9yID0gY2x1c3Rlcl9sYWJlbCksIHNpemUgPSAxLjUpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJDbHVzdGVyaW5nIEZ1bmNpb25hbCBwb3IgQmFycmlvIGNvbiBDdXJ2YXMgUHJvbWVkaW8iLAogICAgeCA9ICJGZWNoYSIsCiAgICB5ID0gIkZyZWN1ZW5jaWEiLAogICAgY29sb3IgPSAiQ2x1c3RlciIKICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKCmBgYAoKIyMjIFZhbGlkYWNpw7NuIGRlIGNsdXN0ZXJzIGNvbiBTaWxob3VldHRlCgpgYGB7cn0KaWYgKCFyZXF1aXJlKCJjbHVzdGVyIikpIGluc3RhbGwucGFja2FnZXMoImNsdXN0ZXIiKQppZiAoIXJlcXVpcmUoImZhY3RvZXh0cmEiKSkgaW5zdGFsbC5wYWNrYWdlcygiZmFjdG9leHRyYSIpCmxpYnJhcnkoY2x1c3RlcikKbGlicmFyeShmYWN0b2V4dHJhKQoKIyBBc2VndXJhciBxdWUgbG9zIGNsdXN0ZXJzIGNvaW5jaWRlbiBjb24gbGEgbWF0cml6CmNsdXN0ZXJzIDwtIGRmX2N1cnZlcyAlPiUKICBkaXN0aW5jdChiYXJyaW8sIGNsdXN0ZXIpICU+JQogIGFycmFuZ2UoYmFycmlvKSAgIyBPcmRlbmFyIHBvciBiYXJyaW8gcGFyYSBnYXJhbnRpemFyIGNvaW5jaWRlbmNpYQoKIyBDYWxjdWxhciBsYSBtYXRyaXogZGUgZGlzdGFuY2lhcwptYXRyaXpfY2x1c3RlcmluZyA8LSBtYXRyaXpfZXZhbHVhZGFbb3JkZXIocm93bmFtZXMobWF0cml6X2V2YWx1YWRhKSksIF0gICMgT3JkZW5hciBmaWxhcyBwb3Igbm9tYnJlIGRlIGJhcnJpbwoKIyBDYWxjdWxhciBlbCDDrW5kaWNlIGRlIFNpbGhvdWV0dGUKc2lsaG91ZXR0ZV9yZXN1bHQgPC0gc2lsaG91ZXR0ZShhcy5udW1lcmljKGNsdXN0ZXJzJGNsdXN0ZXIpLCBkaXN0KG1hdHJpel9jbHVzdGVyaW5nKSkKCiMgUmVzdW1lbiBkZWwgw61uZGljZSBkZSBTaWxob3VldHRlCnN1bW1hcnkoc2lsaG91ZXR0ZV9yZXN1bHQpCgojIEluc3BlY2Npb25hciBsb3MgdmFsb3JlcyBkZWwgw61uZGljZSBkZSBTaWxob3VldHRlCmhlYWQoc2lsaG91ZXR0ZV9yZXN1bHRbLCAxOjNdKSAgIyBDbHVzdGVyLCB2ZWNpbm8gbcOhcyBjZXJjYW5vLCB2YWxvciBkZSBTaWxob3VldHRlCgojIFByb21lZGlvIGRlbCDDrW5kaWNlCm1lYW4oc2lsaG91ZXR0ZV9yZXN1bHRbLCAzXSkKCiMgVmlzdWFsaXphciBTaWxob3VldHRlCmZ2aXpfc2lsaG91ZXR0ZShzaWxob3VldHRlX3Jlc3VsdCkgKwogIGxhYnModGl0bGUgPSAiw41uZGljZSBkZSBTaWxob3VldHRlIHBvciBDbHVzdGVyIiwKICAgICAgIHggPSAiw41uZGljZSBkZSBTaWxob3VldHRlIiwKICAgICAgIHkgPSAiQmFycmlvcyIpICsKICB0aGVtZV9taW5pbWFsKCkKCgpgYGAKCgojIyBjKSBQQ0EgRnVuY2lvbmFsOiBUYW1iacOpbiBhcGxpY2Ftb3MgUENBIGEgbG9zIGNvZWZpY2llbnRlcyBmdW5jaW9uYWxlcyB5IGx1ZWdvIGNhbGN1bGFtb3MgbG9zIGNsdXN0ZXJzCgpgYGB7cn0KCiMgUGFzbyAyOiBFeHRyYWVyIGxvcyBjb2VmaWNpZW50ZXMgZnVuY2lvbmFsZXMKY29lZmljaWVudGVzIDwtIHQoZmRfb2JqX3N1YXZpemFkbyRjb2VmcykKCiMgUGFzbyAzOiBBcGxpY2FyIFBDQSBhIGxvcyBjb2VmaWNpZW50ZXMgZnVuY2lvbmFsZXMKcGNhX3Jlc3VsdCA8LSBwcmNvbXAoY29lZmljaWVudGVzLCBjZW50ZXIgPSBUUlVFLCBzY2FsZS4gPSBUUlVFKSAgIyBOb3JtYWxpemFyIHBhcmEgUENBCgojIEV4cGxvcmFyIGxhIHZhcmlhbnphIGV4cGxpY2FkYSBwb3IgbGFzIGNvbXBvbmVudGVzIHByaW5jaXBhbGVzCnZhcmlhbnphX2V4cGxpY2FkYSA8LSBjdW1zdW0ocGNhX3Jlc3VsdCRzZGV2XjIpIC8gc3VtKHBjYV9yZXN1bHQkc2Rldl4yKSAqIDEwMApwcmludCh2YXJpYW56YV9leHBsaWNhZGEpICAjIFZlciBwcm9wb3JjacOzbiBkZSB2YXJpYW56YSBleHBsaWNhZGEKCiMgRWxlZ2lyIGVsIG7Dum1lcm8gZGUgY29tcG9uZW50ZXMgcHJpbmNpcGFsZXMKbnVtX2NvbXBvbmVudGVzIDwtIDIgICMgQ2FtYmlhciBzZWfDum4gbmVjZXNpZGFkIChlLmcuLCB2YXJpYW56YSBleHBsaWNhZGEgPiA4MCUpCnNjb3Jlc19wY2EgPC0gcGNhX3Jlc3VsdCR4WywgMTpudW1fY29tcG9uZW50ZXNdICAjIFRvbWFyIGxvcyBzY29yZXMgZGUgbGFzIGNvbXBvbmVudGVzIHNlbGVjY2lvbmFkYXMKCgojIFBhc28gNDogQXBsaWNhciBrLW1lYW5zIHVzYW5kbyBsb3Mgc2NvcmVzIGRlIFBDQQpzZXQuc2VlZCgxMjMpICAjIFBhcmEgcmVwcm9kdWNpYmlsaWRhZApudW1fY2x1c3RlcnMgPC0gMiAgIyBDYW1iaWFyIHNlZ8O6biBuZWNlc2lkYWQKa21lYW5zX3Jlc3VsdCA8LSBrbWVhbnMoc2NvcmVzX3BjYSwgY2VudGVycyA9IG51bV9jbHVzdGVycywgbnN0YXJ0ID0gMjApCgojIFZlciBsb3MgY2x1c3RlcnMgYXNpZ25hZG9zCmNsdXN0ZXJzIDwtIGRhdGEuZnJhbWUoCiAgYmFycmlvID0gY29sbmFtZXMoZmRfb2JqX3N1YXZpemFkbyRjb2VmcyksCiAgY2x1c3RlciA9IGttZWFuc19yZXN1bHQkY2x1c3RlcgopCgojIE9yZGVuYXIgZWwgZGF0YWZyYW1lIHBvciBjbHVzdGVyCmNsdXN0ZXJzX29yZGVuYWRvcyA8LSBjbHVzdGVycyAlPiUKICBhcnJhbmdlKGNsdXN0ZXIpCgoKIyBWZXIgbG9zIHByaW1lcm9zIHJlc3VsdGFkb3MKcHJpbnQoY2x1c3RlcnNfb3JkZW5hZG9zKQoKbGlicmFyeShnZ3Bsb3QyKQoKIyBDcmVhciB1biBkYXRhZnJhbWUgY29uIGxhcyBkb3MgcHJpbWVyYXMgY29tcG9uZW50ZXMgcHJpbmNpcGFsZXMgeSBsb3MgY2x1c3RlcnMKZGZfcGNhIDwtIGFzLmRhdGEuZnJhbWUoc2NvcmVzX3BjYSkgICMgQ29udmVydGlyIGxvcyBzY29yZXMgc2VsZWNjaW9uYWRvcyBhIGRhdGFmcmFtZQpkZl9wY2EkY2x1c3RlciA8LSBhcy5mYWN0b3Ioa21lYW5zX3Jlc3VsdCRjbHVzdGVyKSAgIyBBZ3JlZ2FyIGxvcyBjbHVzdGVycyBhc2lnbmFkb3MKZGZfcGNhJGJhcnJpbyA8LSBjb2xuYW1lcyhmZF9vYmpfc3Vhdml6YWRvJGNvZWZzKSAgIyBBZ3JlZ2FyIG5vbWJyZXMgZGUgbG9zIGJhcnJpb3MKCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShkcGx5cikKCiMgQ2FsY3VsYXIgbGEgY2FudGlkYWQgZGUgYmFycmlvcyBwb3IgY2x1c3RlcgpjbHVzdGVyX3NpemVzIDwtIGRmX3BjYSAlPiUKICBncm91cF9ieShjbHVzdGVyKSAlPiUKICBzdW1tYXJpc2Uobl9iYXJyaW9zID0gbigpKSAgIyBDb250YXIgYmFycmlvcyBwb3IgY2x1c3RlcgoKIyBDcmVhciBldGlxdWV0YXMgcGVyc29uYWxpemFkYXMgcGFyYSBsYSBsZXllbmRhCmNsdXN0ZXJfbGFiZWxzIDwtIGNsdXN0ZXJfc2l6ZXMgJT4lCiAgbXV0YXRlKGNsdXN0ZXJfbGFiZWwgPSBwYXN0ZTAoIkNsdXN0ZXIgIiwgY2x1c3RlciwgIiAobj0iLCBuX2JhcnJpb3MsICIpIikpICU+JQogIHB1bGwoY2x1c3Rlcl9sYWJlbCkgICMgRXh0cmFlciBldGlxdWV0YXMgY29tbyB2ZWN0b3IKCiMgTWFwZWFyIGxhcyBldGlxdWV0YXMgYSBsb3MgY2x1c3RlcnMKbmFtZXMoY2x1c3Rlcl9sYWJlbHMpIDwtIGNsdXN0ZXJfc2l6ZXMkY2x1c3RlciAgIyBBc2lnbmFyIGV0aXF1ZXRhcyBwb3IgbsO6bWVybyBkZSBjbHVzdGVyCgojIEdyYWZpY2FyIGxvcyBjbHVzdGVycyBlbiBlbCBlc3BhY2lvIFBDQSBjb24gbGFzIGV0aXF1ZXRhcyBwZXJzb25hbGl6YWRhcwpnZ3Bsb3QoZGZfcGNhLCBhZXMoeCA9IFBDMSwgeSA9IFBDMiwgY29sb3IgPSBjbHVzdGVyKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMsIGFscGhhID0gMC43KSArICAjIFJlcHJlc2VudGFyIGxvcyBwdW50b3MKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJDbHVzdGVycyBWaXN1YWxpemFkb3MgZW4gZWwgRXNwYWNpbyBQQ0EiLAogICAgeCA9ICJDb21wb25lbnRlIFByaW5jaXBhbCAxIiwKICAgIHkgPSAiQ29tcG9uZW50ZSBQcmluY2lwYWwgMiIsCiAgICBjb2xvciA9ICJDbHVzdGVyIgogICkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKAogICAgdmFsdWVzID0gYygiIzFiOWU3NyIsICIjZDk1ZjAyIiwgIiM3NTcwYjMiLCAiI2U3Mjk4YSIpLCAgIyBDb2xvcmVzIHBlcnNvbmFsaXphZG9zCiAgICBsYWJlbHMgPSBjbHVzdGVyX2xhYmVscyAgIyBFdGlxdWV0YXMgY29uIGxhIGNhbnRpZGFkIGRlIGJhcnJpb3MKICApCgoKYGBgCgojIyMgVmFsaWRhY2nDs24gZGUgY2x1c3RlcnMgbnVldmFtZW50ZSBjb24gU2lsaG91ZXR0ZQoKYGBge3J9CmxpYnJhcnkoY2x1c3RlcikKbGlicmFyeShmYWN0b2V4dHJhKQoKIyBDYWxjdWxhciBsYSBtYXRyaXogZGUgZGlzdGFuY2lhcyB1c2FuZG8gbG9zIHNjb3JlcyBQQ0EKZGlzdF9wY2EgPC0gZGlzdChkZl9wY2FbLCBjKCJQQzEiLCAiUEMyIildKQoKIyBDYWxjdWxhciBlbCDDrW5kaWNlIGRlIFNpbGhvdWV0dGUKc2lsaG91ZXR0ZV9yZXN1bHQgPC0gc2lsaG91ZXR0ZShhcy5udW1lcmljKGRmX3BjYSRjbHVzdGVyKSwgZGlzdF9wY2EpCgojIFZlciB1biByZXN1bWVuIGRlbCDDrW5kaWNlIGRlIFNpbGhvdWV0dGUKc3VtbWFyeShzaWxob3VldHRlX3Jlc3VsdCkKCgojIFByb21lZGlvIGRlbCDDrW5kaWNlIGRlIFNpbGhvdWV0dGUKc2lsaG91ZXR0ZV9hdmcgPC0gbWVhbihzaWxob3VldHRlX3Jlc3VsdFssIDNdKQpwcmludChwYXN0ZSgiw41uZGljZSBkZSBTaWxob3VldHRlIHByb21lZGlvOiIsIHJvdW5kKHNpbGhvdWV0dGVfYXZnLCAzKSkpCgoKIyBWaXN1YWxpemFyIGVsIMOtbmRpY2UgZGUgU2lsaG91ZXR0ZQpmdml6X3NpbGhvdWV0dGUoc2lsaG91ZXR0ZV9yZXN1bHQpICsKICBsYWJzKAogICAgdGl0bGUgPSAiw41uZGljZSBkZSBTaWxob3VldHRlIHBhcmEgQ2x1c3RlcnMgZW4gUENBIiwKICAgIHggPSAiw41uZGljZSBkZSBTaWxob3VldHRlIiwKICAgIHkgPSAiQmFycmlvcyIKICApICsKICB0aGVtZV9taW5pbWFsKCkKCmBgYAoK